diff --git a/babel/messages/extract.py b/babel/messages/extract.py index 6be3f477b..476c7c513 100644 --- a/babel/messages/extract.py +++ b/babel/messages/extract.py @@ -22,7 +22,7 @@ import sys from tokenize import generate_tokens, COMMENT, NAME, OP, STRING -from babel.util import parse_encoding, parse_future_flags, pathmatch, has_python_format +from babel.util import parse_encoding, parse_future_flags, pathmatch, has_python_format, has_python_brace_format from textwrap import dedent @@ -480,6 +480,9 @@ def extract_python(fileobj, keywords, comment_tags, options): if has_python_format(message for message in messages if message): flags.add("python-format") + if has_python_brace_format(message for message in messages if message): + flags.add('python-brace-format') + if len(messages) > 1: messages = tuple(messages) else: diff --git a/babel/util.py b/babel/util.py index 3f828149e..0c58dfb3e 100644 --- a/babel/util.py +++ b/babel/util.py @@ -16,6 +16,7 @@ import re import textwrap import pytz as _pytz +import string from babel import localtime missing = object() @@ -281,3 +282,16 @@ def has_python_format(ids): if isinstance(ids, str): ids = [ids] return any(PYTHON_FORMAT.search(id) for id in ids) + + +FORMATTER = string.Formatter() + + +def has_python_brace_format(ids): + if isinstance(ids, str): + ids = [ids] + return any( + field_name + for message_id in ids + for _, field_name, format_spec, conversion in FORMATTER.parse(message_id) + ) diff --git a/tests/test_util.py b/tests/test_util.py index 0a919abbc..e823c5d62 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -18,7 +18,7 @@ import pytest from babel import util -from babel.util import parse_future_flags, has_python_format +from babel.util import parse_future_flags, has_python_format, has_python_brace_format class _FF: @@ -124,6 +124,23 @@ def test_has_python_format(ids): @pytest.mark.parametrize('ids', [ ('foo',), + ('foo {name}',), ]) def test_not_has_python_format(ids): assert not has_python_format(ids) + +@pytest.mark.parametrize('ids', [ + ('foo {name} bar',), + ('foo {name:.3f} bar',), + ('foo {name!r:20} bar',), +]) +def test_has_python_brace_format(ids): + assert has_python_brace_format(ids) + +@pytest.mark.parametrize('ids', [ + ('foo',), + ('fo {}', ), + ('foo %d bar',), +]) +def test_not_has_python_brace_format(ids): + assert not has_python_brace_format(ids)