Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 326c6c4

Browse filesBrowse files
gh-110519: Improve deprecation warning in the gettext module (#110520)
Deprecation warning about non-integer numbers in gettext now always refers to the line in the user code where gettext function or method is used. Previously, it could refer to a line in gettext code. Also, increase test coverage for NullTranslations and domain-aware functions like dngettext().
1 parent 7bd560c commit 326c6c4
Copy full SHA for 326c6c4

File tree

3 files changed

+144
-51
lines changed
Filter options

3 files changed

+144
-51
lines changed

‎Lib/gettext.py

Copy file name to clipboardExpand all lines: Lib/gettext.py
+11-3Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
# find this format documented anywhere.
4747

4848

49+
import operator
4950
import os
5051
import re
5152
import sys
@@ -166,14 +167,21 @@ def _parse(tokens, priority=-1):
166167

167168
def _as_int(n):
168169
try:
169-
i = round(n)
170+
round(n)
170171
except TypeError:
171172
raise TypeError('Plural value must be an integer, got %s' %
172173
(n.__class__.__name__,)) from None
174+
173175
import warnings
176+
frame = sys._getframe(1)
177+
stacklevel = 2
178+
while frame.f_back is not None and frame.f_globals.get('__name__') == __name__:
179+
stacklevel += 1
180+
frame = frame.f_back
174181
warnings.warn('Plural value must be an integer, got %s' %
175182
(n.__class__.__name__,),
176-
DeprecationWarning, 4)
183+
DeprecationWarning,
184+
stacklevel)
177185
return n
178186

179187

@@ -200,7 +208,7 @@ def c2py(plural):
200208
elif c == ')':
201209
depth -= 1
202210

203-
ns = {'_as_int': _as_int}
211+
ns = {'_as_int': _as_int, '__name__': __name__}
204212
exec('''if True:
205213
def func(n):
206214
if not isinstance(n, int):

‎Lib/test/test_gettext.py

Copy file name to clipboardExpand all lines: Lib/test/test_gettext.py
+130-48Lines changed: 130 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import base64
33
import gettext
44
import unittest
5+
from functools import partial
56

67
from test import support
78
from test.support import os_helper
@@ -122,8 +123,9 @@ def reset_gettext():
122123

123124

124125
class GettextBaseTest(unittest.TestCase):
125-
def setUp(self):
126-
self.addCleanup(os_helper.rmtree, os.path.split(LOCALEDIR)[0])
126+
@classmethod
127+
def setUpClass(cls):
128+
cls.addClassCleanup(os_helper.rmtree, os.path.split(LOCALEDIR)[0])
127129
if not os.path.isdir(LOCALEDIR):
128130
os.makedirs(LOCALEDIR)
129131
with open(MOFILE, 'wb') as fp:
@@ -136,6 +138,8 @@ def setUp(self):
136138
fp.write(base64.decodebytes(UMO_DATA))
137139
with open(MMOFILE, 'wb') as fp:
138140
fp.write(base64.decodebytes(MMO_DATA))
141+
142+
def setUp(self):
139143
self.env = self.enterContext(os_helper.EnvironmentVarGuard())
140144
self.env['LANGUAGE'] = 'xx'
141145
reset_gettext()
@@ -316,59 +320,137 @@ def test_multiline_strings(self):
316320
trggrkg zrffntr pngnybt yvoenel.''')
317321

318322

319-
class PluralFormsTestCase(GettextBaseTest):
323+
class PluralFormsTests:
324+
325+
def _test_plural_forms(self, ngettext, gettext,
326+
singular, plural, tsingular, tplural,
327+
numbers_only=True):
328+
x = ngettext(singular, plural, 1)
329+
self.assertEqual(x, tsingular)
330+
x = ngettext(singular, plural, 2)
331+
self.assertEqual(x, tplural)
332+
x = gettext(singular)
333+
self.assertEqual(x, tsingular)
334+
335+
if numbers_only:
336+
lineno = self._test_plural_forms.__code__.co_firstlineno + 9
337+
with self.assertWarns(DeprecationWarning) as cm:
338+
x = ngettext(singular, plural, 1.0)
339+
self.assertEqual(cm.filename, __file__)
340+
self.assertEqual(cm.lineno, lineno + 4)
341+
self.assertEqual(x, tsingular)
342+
with self.assertWarns(DeprecationWarning) as cm:
343+
x = ngettext(singular, plural, 1.1)
344+
self.assertEqual(cm.filename, __file__)
345+
self.assertEqual(cm.lineno, lineno + 9)
346+
self.assertEqual(x, tplural)
347+
with self.assertRaises(TypeError):
348+
ngettext(singular, plural, None)
349+
else:
350+
x = ngettext(singular, plural, None)
351+
self.assertEqual(x, tplural)
352+
353+
def test_plural_forms(self):
354+
self._test_plural_forms(
355+
self.ngettext, self.gettext,
356+
'There is %s file', 'There are %s files',
357+
'Hay %s fichero', 'Hay %s ficheros')
358+
self._test_plural_forms(
359+
self.ngettext, self.gettext,
360+
'%d file deleted', '%d files deleted',
361+
'%d file deleted', '%d files deleted')
362+
363+
def test_plural_context_forms(self):
364+
ngettext = partial(self.npgettext, 'With context')
365+
gettext = partial(self.pgettext, 'With context')
366+
self._test_plural_forms(
367+
ngettext, gettext,
368+
'There is %s file', 'There are %s files',
369+
'Hay %s fichero (context)', 'Hay %s ficheros (context)')
370+
self._test_plural_forms(
371+
ngettext, gettext,
372+
'%d file deleted', '%d files deleted',
373+
'%d file deleted', '%d files deleted')
374+
375+
def test_plural_wrong_context_forms(self):
376+
self._test_plural_forms(
377+
partial(self.npgettext, 'Unknown context'),
378+
partial(self.pgettext, 'Unknown context'),
379+
'There is %s file', 'There are %s files',
380+
'There is %s file', 'There are %s files')
381+
382+
383+
class GNUTranslationsPluralFormsTestCase(PluralFormsTests, GettextBaseTest):
320384
def setUp(self):
321385
GettextBaseTest.setUp(self)
322-
self.localedir = os.curdir
323386
# Set up the bindings
324-
gettext.bindtextdomain('gettext', self.localedir)
387+
gettext.bindtextdomain('gettext', os.curdir)
325388
gettext.textdomain('gettext')
326-
self.mofile = MOFILE
327389

328-
def test_plural_forms1(self):
329-
eq = self.assertEqual
330-
x = gettext.ngettext('There is %s file', 'There are %s files', 1)
331-
eq(x, 'Hay %s fichero')
332-
x = gettext.ngettext('There is %s file', 'There are %s files', 2)
333-
eq(x, 'Hay %s ficheros')
334-
x = gettext.gettext('There is %s file')
335-
eq(x, 'Hay %s fichero')
336-
337-
def test_plural_context_forms1(self):
338-
eq = self.assertEqual
339-
x = gettext.npgettext('With context',
340-
'There is %s file', 'There are %s files', 1)
341-
eq(x, 'Hay %s fichero (context)')
342-
x = gettext.npgettext('With context',
343-
'There is %s file', 'There are %s files', 2)
344-
eq(x, 'Hay %s ficheros (context)')
345-
x = gettext.pgettext('With context', 'There is %s file')
346-
eq(x, 'Hay %s fichero (context)')
347-
348-
def test_plural_forms2(self):
349-
eq = self.assertEqual
350-
with open(self.mofile, 'rb') as fp:
351-
t = gettext.GNUTranslations(fp)
352-
x = t.ngettext('There is %s file', 'There are %s files', 1)
353-
eq(x, 'Hay %s fichero')
354-
x = t.ngettext('There is %s file', 'There are %s files', 2)
355-
eq(x, 'Hay %s ficheros')
356-
x = t.gettext('There is %s file')
357-
eq(x, 'Hay %s fichero')
358-
359-
def test_plural_context_forms2(self):
360-
eq = self.assertEqual
361-
with open(self.mofile, 'rb') as fp:
390+
self.gettext = gettext.gettext
391+
self.ngettext = gettext.ngettext
392+
self.pgettext = gettext.pgettext
393+
self.npgettext = gettext.npgettext
394+
395+
396+
class GNUTranslationsWithDomainPluralFormsTestCase(PluralFormsTests, GettextBaseTest):
397+
def setUp(self):
398+
GettextBaseTest.setUp(self)
399+
# Set up the bindings
400+
gettext.bindtextdomain('gettext', os.curdir)
401+
402+
self.gettext = partial(gettext.dgettext, 'gettext')
403+
self.ngettext = partial(gettext.dngettext, 'gettext')
404+
self.pgettext = partial(gettext.dpgettext, 'gettext')
405+
self.npgettext = partial(gettext.dnpgettext, 'gettext')
406+
407+
def test_plural_forms_wrong_domain(self):
408+
self._test_plural_forms(
409+
partial(gettext.dngettext, 'unknown'),
410+
partial(gettext.dgettext, 'unknown'),
411+
'There is %s file', 'There are %s files',
412+
'There is %s file', 'There are %s files',
413+
numbers_only=False)
414+
415+
def test_plural_context_forms_wrong_domain(self):
416+
self._test_plural_forms(
417+
partial(gettext.dnpgettext, 'unknown', 'With context'),
418+
partial(gettext.dpgettext, 'unknown', 'With context'),
419+
'There is %s file', 'There are %s files',
420+
'There is %s file', 'There are %s files',
421+
numbers_only=False)
422+
423+
424+
class GNUTranslationsClassPluralFormsTestCase(PluralFormsTests, GettextBaseTest):
425+
def setUp(self):
426+
GettextBaseTest.setUp(self)
427+
with open(MOFILE, 'rb') as fp:
362428
t = gettext.GNUTranslations(fp)
363-
x = t.npgettext('With context',
364-
'There is %s file', 'There are %s files', 1)
365-
eq(x, 'Hay %s fichero (context)')
366-
x = t.npgettext('With context',
367-
'There is %s file', 'There are %s files', 2)
368-
eq(x, 'Hay %s ficheros (context)')
369-
x = t.pgettext('With context', 'There is %s file')
370-
eq(x, 'Hay %s fichero (context)')
371429

430+
self.gettext = t.gettext
431+
self.ngettext = t.ngettext
432+
self.pgettext = t.pgettext
433+
self.npgettext = t.npgettext
434+
435+
def test_plural_forms_null_translations(self):
436+
t = gettext.NullTranslations()
437+
self._test_plural_forms(
438+
t.ngettext, t.gettext,
439+
'There is %s file', 'There are %s files',
440+
'There is %s file', 'There are %s files',
441+
numbers_only=False)
442+
443+
def test_plural_context_forms_null_translations(self):
444+
t = gettext.NullTranslations()
445+
self._test_plural_forms(
446+
partial(t.npgettext, 'With context'),
447+
partial(t.pgettext, 'With context'),
448+
'There is %s file', 'There are %s files',
449+
'There is %s file', 'There are %s files',
450+
numbers_only=False)
451+
452+
453+
class PluralFormsInternalTestCase:
372454
# Examples from http://www.gnu.org/software/gettext/manual/gettext.html
373455

374456
def test_ja(self):
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Deprecation warning about non-integer number in :mod:`gettext` now alwais
2+
refers to the line in the user code where gettext function or method is
3+
used. Previously it could refer to a line in ``gettext`` code.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.