You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Argument Clinic: DSLParser.parse_converter() can return an incorrect kwargs dict #106359
The function is annotated as return tuple[str, bool, KwargDict], and KwargDict is a type alias for dict[str, Any]. It's important that the dictionary that's the third element of the tuple only has strings as keys. If it doesn't, then this will fail:
The code as written, however, doesn't guarantee that all the keys in the kwargs dictionary will be strings. In the dictionary comprehension, we can see that annotation is an ast.Call instance. That means that annotation.keywords is of type list[ast.keyword] -- we can see this from typeshed's stubs for the ast module (which are an invaluable reference if you're working with ASTs in Python!): https://github.com/python/typeshed/blob/18d45d62aabe68fce78965c4920cbdeddb4b54db/stdlib/_ast.pyi#L324-L329. If annotation.keywords is of type list[ast.keyword], that means that the node variable in the dictionary comprehension is of type keyword, which means (again using typeshed's ast stubs), that node.arg is of type str | None: https://github.com/python/typeshed/blob/18d45d62aabe68fce78965c4920cbdeddb4b54db/stdlib/_ast.pyi#L516-L520. AKA, the keys in this dictionary are not always guaranteed to be strings -- there's a latent bug in this code!
In the spirit of Argument Clinic, I would think it would be best to catch the error and bail (fail(...)); so a modified variant of the second suggestion, @AlexWaygood
Actually, parse_converter cannot be called unless the param declaration is valid, which means it's guaranteed to be a str. Adding an assert should be sufficient.
--- a/Lib/test/test_clinic.py+++ b/Lib/test/test_clinic.py@@ -327,6 +327,9 @@ def test_param_default_expression(self):
s = self.parse_function_should_fail("module os\nos.access\n follow_symlinks: int = sys.maxsize")
self.assertEqual(s, "Error on line 0:\nWhen you specify a named constant ('sys.maxsize') as your default value,\nyou MUST specify a valid c_default.\n")
+ def test_kwarg_splat(self):+ self.parse_function("module os\nos.access\n follow_symlinks: int(**{'c_default': 'MAXSIZE'})")+
Add this test, and this is the result if you run python -m test test_clinic:
test test_clinic failed -- Traceback (most recent call last):
File "C:\Users\alexw\coding\cpython\Lib\test\test_clinic.py", line 331, in test_kwarg_splatself.parse_function("module os\nos.access\n follow_symlinks: int(**{'c_default': 'MAXSIZE'})")
File "C:\Users\alexw\coding\cpython\Lib\test\test_clinic.py", line 865, in parse_function
block =self.parse(text)
^^^^^^^^^^^^^^^^
File "C:\Users\alexw\coding\cpython\Lib\test\test_clinic.py", line 861, in parse
parser.parse(block)
File "C:\Users\alexw\coding\cpython\Tools\clinic\clinic.py", line 4481, in parseself.state(line)
File "C:\Users\alexw\coding\cpython\Tools\clinic\clinic.py", line 4751, in state_parameters_startreturnself.next(self.state_parameter, line)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\alexw\coding\cpython\Tools\clinic\clinic.py", line 4521, in nextself.state(line)
File "C:\Users\alexw\coding\cpython\Tools\clinic\clinic.py", line 4989, in state_parameter
converter = dict[name](c_name or parameter_name, parameter_name, self.function, value, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^TypeError: keywords must be strings
test_clinic failed (1 error)
== Tests result: FAILURE ==
clinicshould fail if you do a kwarg-splat in a function call as an annotation -- but it should fail gracefully using the fail() function, rather than crashing with a TypeError
Activity
erlend-aasland commentedon Jul 3, 2023
Suggested fixes by Alex: #106354 (comment) (C&P):
Here's two possible ways of fixing this latent bug:
(1)
(2)
You tell me which would be more correct here (or if another solution would be better than either of these)!
erlend-aasland commentedon Jul 3, 2023
In the spirit of Argument Clinic, I would think it would be best to catch the error and bail (
fail(...)); so a modified variant of the second suggestion, @AlexWaygoodAlexWaygood commentedon Jul 3, 2023
Here's a minimal repro of the situation where the
argattribute of anast.keywordnode could beNone:erlend-aasland commentedon Jul 3, 2023
Actually,
parse_convertercannot be called unless the param declaration is valid, which means it's guaranteed to be astr. Adding anassertshould be sufficient.AlexWaygood commentedon Jul 3, 2023
And here's a failing test case:
Add this test, and this is the result if you run
python -m test test_clinic:clinicshould fail if you do a kwarg-splat in a function call as an annotation -- but it should fail gracefully using thefail()function, rather than crashing with aTypeErrorerlend-aasland commentedon Jul 3, 2023
I've failed to reproduce it in clinic, though.
AlexWaygood commentedon Jul 3, 2023
I just did; see #106359 (comment)
pythongh-106359: Fix corner case bugs in Argument Clinic converter pa…
erlend-aasland commentedon Jul 3, 2023
Yeah, I managed to do a similar repro; PR up.
AlexWaygood commentedon Jul 3, 2023
Dang, you beat me to it; I was also working on a PR :D
erlend-aasland commentedon Jul 3, 2023
Sorry! :) Oh, well you kinda wrote the entire patch. I just filled in a tiny blank!
gh-106359: Fix corner case bugs in Argument Clinic converter parser (#…
pythongh-106359: Fix corner case bugs in Argument Clinic converter pa…
[3.12] gh-106359: Fix corner case bugs in Argument Clinic converter p…
Merge branch 'main' into modulelocals