Skip to content

Commit 8d8c3cf

Browse files
committed
fix(catalog): refine PYTHON_FORMAT regex to avoid prose false positives
Allow space flag only when followed by explicit width or precision. Add test cases in test_catalog.py and test_checkers.py.
1 parent 259ecfc commit 8d8c3cf

3 files changed

Lines changed: 22 additions & 3 deletions

File tree

babel/messages/catalog.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ def get_close_matches(word, possibilities, n=3, cutoff=0.6):
7474
\%
7575
(?:\(([\w]*)\))?
7676
(
77-
[-#0+]?(?:\*|[\d]+)?
78-
(?:\.(?:\*|[\d]+))?
79-
[hlL]?
77+
(?:[-#0+]?(?:\*|[\d]+)?(?:\.(?:\*|[\d]+))?)
78+
|
79+
(?:\ +(?:\.[\d]+|[\d]+(?:\.[\d]+)?))
8080
)
81+
[hlL]?
8182
([diouxXeEfFgGcrs%])
8283
''',
8384
re.VERBOSE,

tests/messages/test_catalog.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ def test_message_python_format():
3838
assert catalog.PYTHON_FORMAT.search('foo %()s')
3939

4040

41+
def test_message_python_format_prose_percent():
42+
# literal percent signs in prose must not be mistaken for placeholders
43+
assert not catalog.PYTHON_FORMAT.search('100 % done')
44+
assert not catalog.PYTHON_FORMAT.search('50 % off')
45+
assert not catalog.PYTHON_FORMAT.search('10% of')
46+
assert not catalog.PYTHON_FORMAT.search('10% der')
47+
# space-flag placeholders with explicit width/precision are still valid
48+
assert catalog.PYTHON_FORMAT.search('% 5d')
49+
assert catalog.PYTHON_FORMAT.search('% .2f')
50+
assert catalog.PYTHON_FORMAT.search('% 5.2f')
51+
52+
4153
def test_message_python_brace_format():
4254
assert not catalog._has_python_brace_format('')
4355
assert not catalog._has_python_brace_format('foo')

tests/messages/test_checkers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ def test_python_format_valid(msgid, msgstr):
390390
'%(foo)d',
391391
"incompatible format for placeholder 'foo': 'd' and 's' are not compatible",
392392
),
393+
('% 5d', '% 5s', "incompatible format for placeholder 1: 'd' and 's' are not compatible"),
393394
],
394395
)
395396
def test__validate_format_invalid(msgid, msgstr, error):
@@ -412,6 +413,11 @@ def test__validate_format_invalid(msgid, msgstr, error):
412413
('%(foo)s', 'foo'),
413414
('%(foo)s', '%(foo)s %(foo)s'),
414415
('%(bar)s foo %(n)d', '%(n)d foo %(bar)s'),
416+
# literal percent signs in prose are not placeholders
417+
('100 % done', '100 % erledigt'),
418+
('50 % off', '50 % Rabatt'),
419+
# space-flag placeholders with explicit width/precision are still checked
420+
('% 5d', '% 5d'),
415421
],
416422
)
417423
def test__validate_format_valid(msgid, msgstr):

0 commit comments

Comments
 (0)