Skip to content

Commit bf40fdb

Browse files
[3.13] gh-126807: pygettext: Do not attempt to extract messages from function definitions. (GH-126808) (GH-126846)
Fixes a bug where pygettext would attempt to extract a message from a code like this: def _(x): pass This is because pygettext only looks at one token at a time and '_(x)' looks like a function call. However, since 'x' is not a string literal, it would erroneously issue a warning. (cherry picked from commit 9a45638) Co-authored-by: Tomas R <[email protected]>
1 parent bf6fa21 commit bf40fdb

File tree

3 files changed

+36
-5
lines changed

3 files changed

+36
-5
lines changed

Lib/test/test_tools/test_i18n.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,23 @@ def assert_POT_equal(self, expected, actual):
8787
self.maxDiff = None
8888
self.assertEqual(normalize_POT_file(expected), normalize_POT_file(actual))
8989

90-
def extract_docstrings_from_str(self, module_content):
91-
""" utility: return all msgids extracted from module_content """
92-
filename = 'test_docstrings.py'
93-
with temp_cwd(None) as cwd:
90+
def extract_from_str(self, module_content, *, args=(), strict=True):
91+
"""Return all msgids extracted from module_content."""
92+
filename = 'test.py'
93+
with temp_cwd(None):
9494
with open(filename, 'w', encoding='utf-8') as fp:
9595
fp.write(module_content)
96-
assert_python_ok('-Xutf8', self.script, '-D', filename)
96+
res = assert_python_ok('-Xutf8', self.script, *args, filename)
97+
if strict:
98+
self.assertEqual(res.err, b'')
9799
with open('messages.pot', encoding='utf-8') as fp:
98100
data = fp.read()
99101
return self.get_msgids(data)
100102

103+
def extract_docstrings_from_str(self, module_content):
104+
"""Return all docstrings extracted from module_content."""
105+
return self.extract_from_str(module_content, args=('--docstrings',), strict=False)
106+
101107
def test_header(self):
102108
"""Make sure the required fields are in the header, according to:
103109
http://www.gnu.org/software/gettext/manual/gettext.html#Header-Entry
@@ -344,6 +350,23 @@ def test_calls_in_fstring_with_partially_wrong_expression(self):
344350
self.assertNotIn('foo', msgids)
345351
self.assertIn('bar', msgids)
346352

353+
def test_function_and_class_names(self):
354+
"""Test that function and class names are not mistakenly extracted."""
355+
msgids = self.extract_from_str(dedent('''\
356+
def _(x):
357+
pass
358+
359+
def _(x="foo"):
360+
pass
361+
362+
async def _(x):
363+
pass
364+
365+
class _(object):
366+
pass
367+
'''))
368+
self.assertEqual(msgids, [''])
369+
347370
def test_pygettext_output(self):
348371
"""Test that the pygettext output exactly matches snapshots."""
349372
for input_file in DATA_DIR.glob('*.py'):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix extraction warnings in :program:`pygettext.py` caused by mistaking
2+
function definitions for function calls.

Tools/i18n/pygettext.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ def __waiting(self, ttype, tstring, lineno):
341341
if ttype == tokenize.NAME and tstring in ('class', 'def'):
342342
self.__state = self.__suiteseen
343343
return
344+
if ttype == tokenize.NAME and tstring in ('class', 'def'):
345+
self.__state = self.__ignorenext
346+
return
344347
if ttype == tokenize.NAME and tstring in opts.keywords:
345348
self.__state = self.__keywordseen
346349
return
@@ -448,6 +451,9 @@ def __openseen(self, ttype, tstring, lineno):
448451
}, file=sys.stderr)
449452
self.__state = self.__waiting
450453

454+
def __ignorenext(self, ttype, tstring, lineno):
455+
self.__state = self.__waiting
456+
451457
def __addentry(self, msg, lineno=None, isdocstring=0):
452458
if lineno is None:
453459
lineno = self.__lineno

0 commit comments

Comments
 (0)