Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix extracted lineno with nested calls #1126

Merged
merged 1 commit into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions babel/messages/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from functools import lru_cache
from os.path import relpath
from textwrap import dedent
from tokenize import COMMENT, NAME, OP, STRING, generate_tokens
from tokenize import COMMENT, NAME, NL, OP, STRING, generate_tokens
from typing import TYPE_CHECKING, Any

from babel.messages._compat import find_entrypoints
Expand Down Expand Up @@ -530,7 +530,6 @@
in_def = False
continue
if funcname:
message_lineno = lineno
call_stack += 1
elif in_def and tok == OP and value == ':':
# End of a class definition without parens
Expand Down Expand Up @@ -580,11 +579,15 @@
elif tok == STRING:
val = _parse_python_string(value, encoding, future_flags)
if val is not None:
if not message_lineno:
message_lineno = lineno
buf.append(val)

# Python 3.12+, see https://peps.python.org/pep-0701/#new-tokens
elif tok == FSTRING_START:
current_fstring_start = value
if not message_lineno:
message_lineno = lineno

Check warning on line 590 in babel/messages/extract.py

View check run for this annotation

Codecov / codecov/patch

babel/messages/extract.py#L589-L590

Added lines #L589 - L590 were not covered by tests
elif tok == FSTRING_MIDDLE:
if current_fstring_start is not None:
current_fstring_start += value
Expand All @@ -608,6 +611,9 @@
# for the comment to still be a valid one
old_lineno, old_comment = translator_comments.pop()
translator_comments.append((old_lineno + 1, old_comment))

elif tok != NL and not message_lineno:
message_lineno = lineno
elif call_stack > 0 and tok == OP and value == ')':
call_stack -= 1
elif funcname and call_stack == -1:
Expand Down
10 changes: 8 additions & 2 deletions tests/messages/test_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ def test_nested_calls(self):
msg8 = gettext('Rabbit')
msg9 = dgettext('wiki', model.addPage())
msg10 = dngettext(getDomain(), 'Page', 'Pages', 3)
msg11 = ngettext(
"bunny",
"bunnies",
len(bunnies)
)
""")
messages = list(extract.extract_python(buf,
extract.DEFAULT_KEYWORDS.keys(),
Expand All @@ -49,6 +54,7 @@ def test_nested_calls(self):
(8, 'gettext', 'Rabbit', []),
(9, 'dgettext', ('wiki', None), []),
(10, 'dngettext', (None, 'Page', 'Pages', None), []),
(12, 'ngettext', ('bunny', 'bunnies', None), []),
]

def test_extract_default_encoding_ascii(self):
Expand Down Expand Up @@ -97,10 +103,10 @@ def test_comments_with_calls_that_spawn_multiple_lines(self):
messages = list(extract.extract_python(buf, ('ngettext', '_'), ['NOTE:'],

{'strip_comment_tags': False}))
assert messages[0] == (3, 'ngettext', ('Catalog deleted.', 'Catalogs deleted.', None), ['NOTE: This Comment SHOULD Be Extracted'])
assert messages[0] == (2, 'ngettext', ('Catalog deleted.', 'Catalogs deleted.', None), ['NOTE: This Comment SHOULD Be Extracted'])
assert messages[1] == (6, '_', 'Locale deleted.', ['NOTE: This Comment SHOULD Be Extracted'])
assert messages[2] == (10, 'ngettext', ('Foo deleted.', 'Foos deleted.', None), ['NOTE: This Comment SHOULD Be Extracted'])
assert messages[3] == (15, 'ngettext', ('Bar deleted.', 'Bars deleted.', None), ['NOTE: This Comment SHOULD Be Extracted', 'NOTE: And This One Too'])
assert messages[3] == (14, 'ngettext', ('Bar deleted.', 'Bars deleted.', None), ['NOTE: This Comment SHOULD Be Extracted', 'NOTE: And This One Too'])

def test_declarations(self):
buf = BytesIO(b"""\
Expand Down
Loading