Skip to content

Commit 08512c7

Browse files
committed
Add support for python-brace-format
1 parent c2b397e commit 08512c7

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

babel/messages/catalog.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from difflib import SequenceMatcher
1717
from email import message_from_string
1818
from heapq import nlargest
19+
from string import Formatter
1920
from typing import TYPE_CHECKING
2021

2122
from babel import __version__ as VERSION
@@ -69,6 +70,15 @@ def get_close_matches(word, possibilities, n=3, cutoff=0.6):
6970
''', re.VERBOSE)
7071

7172

73+
def _has_python_brace_format(string: str) -> bool:
74+
fmt = Formatter()
75+
try:
76+
parsed = list(fmt.parse(string))
77+
except ValueError:
78+
return False
79+
return any(True for _, field_name, *_ in parsed if field_name is not None)
80+
81+
7282
def _parse_datetime_header(value: str) -> datetime.datetime:
7383
match = re.match(r'^(?P<datetime>.*?)(?P<tzoffset>[+-]\d{4})?$', value)
7484

@@ -140,6 +150,10 @@ def __init__(
140150
self.flags.add('python-format')
141151
else:
142152
self.flags.discard('python-format')
153+
if id and self.python_brace_format:
154+
self.flags.add('python-brace-format')
155+
else:
156+
self.flags.discard('python-brace-format')
143157
self.auto_comments = list(distinct(auto_comments))
144158
self.user_comments = list(distinct(user_comments))
145159
if isinstance(previous_id, str):
@@ -252,6 +266,21 @@ def python_format(self) -> bool:
252266
ids = [ids]
253267
return any(PYTHON_FORMAT.search(id) for id in ids)
254268

269+
@property
270+
def python_brace_format(self) -> bool:
271+
"""Whether the message contains Python f-string parameters.
272+
273+
>>> Message('Hello, {name}!').python_brace_format
274+
True
275+
>>> Message(('One apple', '{count} apples')).python_brace_format
276+
True
277+
278+
:type: `bool`"""
279+
ids = self.id
280+
if not isinstance(ids, (list, tuple)):
281+
ids = [ids]
282+
return any(_has_python_brace_format(id) for id in ids)
283+
255284

256285
class TranslationError(Exception):
257286
"""Exception thrown by translation checkers when invalid message

tests/messages/test_catalog.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ def test_python_format(self):
3939
assert catalog.PYTHON_FORMAT.search('foo %(name)*.*f')
4040
assert catalog.PYTHON_FORMAT.search('foo %()s')
4141

42+
def test_python_brace_format(self):
43+
assert not catalog._has_python_brace_format('')
44+
assert not catalog._has_python_brace_format('foo')
45+
assert not catalog._has_python_brace_format('{')
46+
assert not catalog._has_python_brace_format('}')
47+
assert not catalog._has_python_brace_format('{} {')
48+
assert not catalog._has_python_brace_format('{{}}')
49+
assert catalog._has_python_brace_format('{}')
50+
assert catalog._has_python_brace_format('foo {name}')
51+
assert catalog._has_python_brace_format('foo {name!s}')
52+
assert catalog._has_python_brace_format('foo {name!r}')
53+
assert catalog._has_python_brace_format('foo {name!a}')
54+
assert catalog._has_python_brace_format('foo {name!r:10}')
55+
assert catalog._has_python_brace_format('foo {name!r:10.2}')
56+
assert catalog._has_python_brace_format('foo {name!r:10.2f}')
57+
assert catalog._has_python_brace_format('foo {name!r:10.2f} {name!r:10.2f}')
58+
assert catalog._has_python_brace_format('foo {name!r:10.2f=}')
59+
4260
def test_translator_comments(self):
4361
mess = catalog.Message('foo', user_comments=['Comment About `foo`'])
4462
assert mess.user_comments == ['Comment About `foo`']
@@ -342,10 +360,19 @@ def test_message_pluralizable():
342360

343361

344362
def test_message_python_format():
363+
assert not catalog.Message('foo').python_format
364+
assert not catalog.Message(('foo', 'foo')).python_format
345365
assert catalog.Message('foo %(name)s bar').python_format
346366
assert catalog.Message(('foo %(name)s', 'foo %(name)s')).python_format
347367

348368

369+
def test_message_python_brace_format():
370+
assert not catalog.Message('foo').python_brace_format
371+
assert not catalog.Message(('foo', 'foo')).python_brace_format
372+
assert catalog.Message('foo {name} bar').python_brace_format
373+
assert catalog.Message(('foo {name}', 'foo {name}')).python_brace_format
374+
375+
349376
def test_catalog():
350377
cat = catalog.Catalog(project='Foobar', version='1.0',
351378
copyright_holder='Foo Company')

0 commit comments

Comments
 (0)