Skip to content

Commit 0da4c88

Browse files
gh-106359: Fix corner case bugs in Argument Clinic converter parser (#106361)
DSLParser.parse_converter() could return unusable kwdicts in some rare cases Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
1 parent 60e41a0 commit 0da4c88

File tree

3 files changed

+27
-7
lines changed

3 files changed

+27
-7
lines changed

Lib/test/test_clinic.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,22 @@ def test_other_bizarre_things_in_annotations_fail(self):
813813
)
814814
self.assertEqual(s, expected_failure_message)
815815

816+
def test_kwarg_splats_disallowed_in_function_call_annotations(self):
817+
expected_error_msg = (
818+
"Error on line 0:\n"
819+
"Cannot use a kwarg splat in a function-call annotation\n"
820+
)
821+
dataset = (
822+
'module fo\nfo.barbaz\n o: bool(**{None: "bang!"})',
823+
'module fo\nfo.barbaz -> bool(**{None: "bang!"})',
824+
'module fo\nfo.barbaz -> bool(**{"bang": 42})',
825+
'module fo\nfo.barbaz\n o: bool(**{"bang": None})',
826+
)
827+
for fn in dataset:
828+
with self.subTest(fn=fn):
829+
out = self.parse_function_should_fail(fn)
830+
self.assertEqual(out, expected_error_msg)
831+
816832
def test_unused_param(self):
817833
block = self.parse("""
818834
module foo
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Argument Clinic now explicitly forbids "kwarg splats" in function calls used as
2+
annotations.

Tools/clinic/clinic.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4291,6 +4291,7 @@ def dedent(self, line):
42914291

42924292

42934293
StateKeeper = Callable[[str | None], None]
4294+
ConverterArgs = dict[str, Any]
42944295

42954296
class DSLParser:
42964297
function: Function | None
@@ -5033,21 +5034,22 @@ def bad_node(self, node):
50335034
key = f"{parameter_name}_as_{c_name}" if c_name else parameter_name
50345035
self.function.parameters[key] = p
50355036

5036-
KwargDict = dict[str | None, Any]
5037-
50385037
@staticmethod
5039-
def parse_converter(annotation: ast.expr | None) -> tuple[str, bool, KwargDict]:
5038+
def parse_converter(
5039+
annotation: ast.expr | None
5040+
) -> tuple[str, bool, ConverterArgs]:
50405041
match annotation:
50415042
case ast.Constant(value=str() as value):
50425043
return value, True, {}
50435044
case ast.Name(name):
50445045
return name, False, {}
50455046
case ast.Call(func=ast.Name(name)):
50465047
symbols = globals()
5047-
kwargs = {
5048-
node.arg: eval_ast_expr(node.value, symbols)
5049-
for node in annotation.keywords
5050-
}
5048+
kwargs: ConverterArgs = {}
5049+
for node in annotation.keywords:
5050+
if not isinstance(node.arg, str):
5051+
fail("Cannot use a kwarg splat in a function-call annotation")
5052+
kwargs[node.arg] = eval_ast_expr(node.value, symbols)
50515053
return name, False, kwargs
50525054
case _:
50535055
fail(

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy