]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-106359: Fix corner case bugs in Argument Clinic converter parser (GH-106361...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Mon, 3 Jul 2023 13:42:20 +0000 (06:42 -0700)
committerGitHub <noreply@github.com>
Mon, 3 Jul 2023 13:42:20 +0000 (13:42 +0000)
gh-106359: Fix corner case bugs in Argument Clinic converter parser (GH-106361)

DSLParser.parse_converter() could return unusable kwdicts in some rare cases

(cherry picked from commit 0da4c883cf4185efe27b711c3e0a1e6e94397610)

Co-authored-by: Erlend E. Aasland <erlend@python.org>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Lib/test/test_clinic.py
Misc/NEWS.d/next/Tools-Demos/2023-07-03-14-06-19.gh-issue-106359.RfJuR0.rst [new file with mode: 0644]
Tools/clinic/clinic.py

index bea7303805567f8e4572b8e3814a80bf0877a6ff..76a06df5fe2e5511dd690efeebbd65dcd5e8bb88 100644 (file)
@@ -813,6 +813,22 @@ Annotations must be either a name, a function call, or a string.
         )
         self.assertEqual(s, expected_failure_message)
 
+    def test_kwarg_splats_disallowed_in_function_call_annotations(self):
+        expected_error_msg = (
+            "Error on line 0:\n"
+            "Cannot use a kwarg splat in a function-call annotation\n"
+        )
+        dataset = (
+            'module fo\nfo.barbaz\n   o: bool(**{None: "bang!"})',
+            'module fo\nfo.barbaz -> bool(**{None: "bang!"})',
+            'module fo\nfo.barbaz -> bool(**{"bang": 42})',
+            'module fo\nfo.barbaz\n   o: bool(**{"bang": None})',
+        )
+        for fn in dataset:
+            with self.subTest(fn=fn):
+                out = self.parse_function_should_fail(fn)
+                self.assertEqual(out, expected_error_msg)
+
     def test_unused_param(self):
         block = self.parse("""
             module foo
diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-07-03-14-06-19.gh-issue-106359.RfJuR0.rst b/Misc/NEWS.d/next/Tools-Demos/2023-07-03-14-06-19.gh-issue-106359.RfJuR0.rst
new file mode 100644 (file)
index 0000000..600c265
--- /dev/null
@@ -0,0 +1,2 @@
+Argument Clinic now explicitly forbids "kwarg splats" in function calls used as
+annotations.
index d182e5e7764e46e6078da4f899d220d2bd99cdf0..d67479c7f8ab884532c221979d850436364fdb1a 100755 (executable)
@@ -4289,6 +4289,7 @@ class IndentStack:
 
 
 StateKeeper = Callable[[str | None], None]
+ConverterArgs = dict[str, Any]
 
 class DSLParser:
     def __init__(self, clinic: Clinic) -> None:
@@ -5016,10 +5017,10 @@ class DSLParser:
         key = f"{parameter_name}_as_{c_name}" if c_name else parameter_name
         self.function.parameters[key] = p
 
-    KwargDict = dict[str | None, Any]
-
     @staticmethod
-    def parse_converter(annotation: ast.expr | None) -> tuple[str, bool, KwargDict]:
+    def parse_converter(
+            annotation: ast.expr | None
+    ) -> tuple[str, bool, ConverterArgs]:
         match annotation:
             case ast.Constant(value=str() as value):
                 return value, True, {}
@@ -5027,10 +5028,11 @@ class DSLParser:
                 return name, False, {}
             case ast.Call(func=ast.Name(name)):
                 symbols = globals()
-                kwargs = {
-                    node.arg: eval_ast_expr(node.value, symbols)
-                    for node in annotation.keywords
-                }
+                kwargs: ConverterArgs = {}
+                for node in annotation.keywords:
+                    if not isinstance(node.arg, str):
+                        fail("Cannot use a kwarg splat in a function-call annotation")
+                    kwargs[node.arg] = eval_ast_expr(node.value, symbols)
                 return name, False, kwargs
             case _:
                 fail(