From 21069237af0af35d3e8ad70fb528bd757def9b02 Mon Sep 17 00:00:00 2001 From: James McKinney <26463+jpmckinney@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:50:54 -0400 Subject: [PATCH 1/2] fix: check_and_call_extract_file uses the first matching method and options, instead of the first matching method and last matching options --- babel/messages/extract.py | 1 + 1 file changed, 1 insertion(+) diff --git a/babel/messages/extract.py b/babel/messages/extract.py index 1b2a37fc6..94221df75 100644 --- a/babel/messages/extract.py +++ b/babel/messages/extract.py @@ -276,6 +276,7 @@ def check_and_call_extract_file( for opattern, odict in options_map.items(): if pathmatch(opattern, filename): options = odict + break if callback: callback(filename, method, options) for message_tuple in extract_from_file( From 4856358078fafa20dd6f91cbc53065aa70a17f55 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 9 Dec 2024 15:19:37 +0200 Subject: [PATCH 2/2] Add test for PR 1121 --- tests/messages/test_frontend.py | 52 ++++++++++++++++++++++++++++----- tests/messages/utils.py | 7 +++++ 2 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 tests/messages/utils.py diff --git a/tests/messages/test_frontend.py b/tests/messages/test_frontend.py index 7a6b08c44..b05f9f683 100644 --- a/tests/messages/test_frontend.py +++ b/tests/messages/test_frontend.py @@ -45,6 +45,7 @@ project_dir, this_dir, ) +from tests.messages.utils import CUSTOM_EXTRACTOR_COOKIE def _po_file(locale): @@ -1392,7 +1393,11 @@ def test_update_init_missing(self): mapping_cfg = """ [extractors] -custom = mypackage.module:myfunc +custom = tests.messages.utils:custom_extractor + +# Special extractor for a given Python file +[custom: special.py] +treat = delicious # Python source files [python: **.py] @@ -1411,7 +1416,13 @@ def test_update_init_missing(self): mapping_toml = """ [extractors] -custom = "mypackage.module:myfunc" +custom = "tests.messages.utils:custom_extractor" + +# Special extractor for a given Python file +[[mappings]] +method = "custom" +pattern = "special.py" +treat = "delightful" # Python source files [[mappings]] @@ -1470,18 +1481,17 @@ def test_parse_mapping(data: str, parser, preprocess, is_toml): buf = StringIO(data) method_map, options_map = parser(buf) - assert len(method_map) == 4 + assert len(method_map) == 5 - assert method_map[0] == ('**.py', 'python') + assert method_map[1] == ('**.py', 'python') assert options_map['**.py'] == {} - assert method_map[1] == ('**/templates/**.html', 'genshi') + assert method_map[2] == ('**/templates/**.html', 'genshi') assert options_map['**/templates/**.html']['include_attrs'] == '' - assert method_map[2] == ('**/templates/**.txt', 'genshi') + assert method_map[3] == ('**/templates/**.txt', 'genshi') assert (options_map['**/templates/**.txt']['template_class'] == 'genshi.template:TextTemplate') assert options_map['**/templates/**.txt']['encoding'] == 'latin-1' - - assert method_map[3] == ('**/custom/*.*', 'mypackage.module:myfunc') + assert method_map[4] == ('**/custom/*.*', 'tests.messages.utils:custom_extractor') assert options_map['**/custom/*.*'] == {} @@ -1663,3 +1673,29 @@ def test_extract_header_comment(monkeypatch, tmp_path): cmdinst.run() pot_content = pot_file.read_text() assert 'Boing' in pot_content + + +@pytest.mark.parametrize("mapping_format", ("toml", "cfg")) +def test_pr_1121(tmp_path, monkeypatch, caplog, mapping_format): + """ + Test that extraction uses the first matching method and options, + instead of the first matching method and last matching options. + + Without the fix in PR #1121, this test would fail, + since the `custom_extractor` isn't passed a delicious treat via + the configuration. + """ + if mapping_format == "cfg": + mapping_file = (tmp_path / "mapping.cfg") + mapping_file.write_text(mapping_cfg) + else: + mapping_file = (tmp_path / "mapping.toml") + mapping_file.write_text(mapping_toml) + (tmp_path / "special.py").write_text("# this file is special") + pot_path = (tmp_path / "output.pot") + monkeypatch.chdir(tmp_path) + cmdinst = configure_cli_command(f"extract . -o {shlex.quote(str(pot_path))} --mapping {shlex.quote(mapping_file.name)}") + assert isinstance(cmdinst, ExtractMessages) + cmdinst.run() + # If the custom extractor didn't run, we wouldn't see the cookie in there. + assert CUSTOM_EXTRACTOR_COOKIE in pot_path.read_text() diff --git a/tests/messages/utils.py b/tests/messages/utils.py new file mode 100644 index 000000000..d0797a337 --- /dev/null +++ b/tests/messages/utils.py @@ -0,0 +1,7 @@ +CUSTOM_EXTRACTOR_COOKIE = "custom extractor was here" + + +def custom_extractor(fileobj, keywords, comment_tags, options): + if "treat" not in options: + raise RuntimeError(f"The custom extractor refuses to run without a delicious treat; got {options!r}") + return [(1, next(iter(keywords)), (CUSTOM_EXTRACTOR_COOKIE,), [])]