Skip to content

Commit 851f63c

Browse files
pythongh-106359: Fix corner case bugs in Argument Clinic converter parser
DSLParser.parse_converter() could return unusable kwdicts in some rare cases
1 parent 60e41a0 commit 851f63c

File tree

2 files changed

+19
-21
lines changed

2 files changed

+19
-21
lines changed

Lib/test/test_clinic.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -797,21 +797,18 @@ def test_other_bizarre_things_in_annotations_fail(self):
797797
Error on line 0:
798798
Annotations must be either a name, a function call, or a string.
799799
"""
800-
801-
s = self.parse_function_should_fail(
802-
'module os\nos.access\n path: {"some": "dictionary"}'
803-
)
804-
self.assertEqual(s, expected_failure_message)
805-
806-
s = self.parse_function_should_fail(
807-
'module os\nos.access\n path: ["list", "of", "strings"]'
800+
dataset = (
801+
'module os\nos.access\n path: {"some": "dictionary"}',
802+
'module os\nos.access\n path: ["list", "of", "strings"]',
803+
'module os\nos.access\n path: (x for x in range(42))',
804+
'module fo\nfo.barbaz\n o: bool(**{None: "bang!"})',
805+
'module fo\nfo.barbaz -> bool(**{None: "bang!"})',
808806
)
809-
self.assertEqual(s, expected_failure_message)
810807

811-
s = self.parse_function_should_fail(
812-
'module os\nos.access\n path: (x for x in range(42))'
813-
)
814-
self.assertEqual(s, expected_failure_message)
808+
for fn in dataset:
809+
with self.subTest(fn=fn):
810+
actual = self.parse_function_should_fail(fn)
811+
self.assertEqual(actual, expected_failure_message)
815812

816813
def test_unused_param(self):
817814
block = self.parse("""

Tools/clinic/clinic.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5033,26 +5033,27 @@ def bad_node(self, node):
50335033
key = f"{parameter_name}_as_{c_name}" if c_name else parameter_name
50345034
self.function.parameters[key] = p
50355035

5036-
KwargDict = dict[str | None, Any]
5036+
KwargDict = dict[str, Any]
50375037

50385038
@staticmethod
50395039
def parse_converter(annotation: ast.expr | None) -> tuple[str, bool, KwargDict]:
5040+
msg = "Annotations must be either a name, a function call, or a string."
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: dict[str, Any] = {}
5049+
for node in annotation.keywords:
5050+
if not isinstance(node.arg, str):
5051+
fail(msg)
5052+
kwargs[node.arg] = eval_ast_expr(node.value, symbols)
5053+
print(kwargs)
50515054
return name, False, kwargs
50525055
case _:
5053-
fail(
5054-
"Annotations must be either a name, a function call, or a string."
5055-
)
5056+
fail(msg)
50565057

50575058
def parse_special_symbol(self, symbol):
50585059
if symbol == '*':

0 commit comments

Comments
 (0)