]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
fix(extract): use the first matching method and options (#1121)
authorJames McKinney <26463+jpmckinney@users.noreply.github.com>
Mon, 9 Dec 2024 14:39:49 +0000 (09:39 -0500)
committerGitHub <noreply@github.com>
Mon, 9 Dec 2024 14:39:49 +0000 (14:39 +0000)
... instead of the first matching method and last matching options.

Co-authored-by: Aarni Koskela <akx@iki.fi>
babel/messages/extract.py
tests/messages/test_frontend.py
tests/messages/utils.py [new file with mode: 0644]

index 1b2a37fc67579f791657217c7c722951edec98ba..94221df75c1b8358f94c22b3217b22f8e473c34d 100644 (file)
@@ -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(
index 7a6b08c4418906e71d0afe325c602d8899391a32..b05f9f683edf65c056d1a11d57be3d6834d61a25 100644 (file)
@@ -45,6 +45,7 @@ from tests.messages.consts import (
     project_dir,
     this_dir,
 )
+from tests.messages.utils import CUSTOM_EXTRACTOR_COOKIE
 
 
 def _po_file(locale):
@@ -1392,7 +1393,11 @@ msgstr[2] ""
 
 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 @@ encoding = latin-1
 
 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 (file)
index 0000000..d0797a3
--- /dev/null
@@ -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,), [])]