-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-104050: Add more type hints to Argument Clinic DSLParser() #106354
gh-104050: Add more type hints to Argument Clinic DSLParser() #106354
Conversation
Add basic annotations to state_modulename_name()
My basal typing skills are no longer sufficient; I need help! 😱 cc. @AlexWaygood Tools/clinic/clinic.py:4593: error: Incompatible types in assignment (expression has type "str | None", variable has type "str") [assignment]
Tools/clinic/clinic.py:4617: error: Keywords must be strings [misc]
Tools/clinic/clinic.py:4645: error: Incompatible types in assignment (expression has type "Function", variable has type "None") [assignment]
Tools/clinic/clinic.py:4646: error: Argument "return_converter" to "Function" has incompatible type "CReturnConverter"; expected "Callable[..., CReturnConverter]" [arg-type]
Tools/clinic/clinic.py:4654: error: "None" has no attribute "self_converter" [attr-defined]
Tools/clinic/clinic.py:4654: error: Keywords must be strings [misc]
Tools/clinic/clinic.py:4655: error: Argument 1 to "Parameter" has incompatible type "str | None"; expected "str" [arg-type]
Tools/clinic/clinic.py:4655: error: Argument 2 to "Parameter" has incompatible type "Literal[_ParameterKind.POSITIONAL_ONLY]"; expected "str" [arg-type]
Tools/clinic/clinic.py:4655: error: Argument "function" to "Parameter" has incompatible type "None"; expected "Function" [arg-type]
Tools/clinic/clinic.py:4656: error: "None" has no attribute "parameters" [attr-defined]
Tools/clinic/clinic.py:4659: error: Argument 1 to "next" of "DSLParser" has incompatible type "Callable[[str], None]"; expected "Callable[[str | None], None]" [arg-type]
Found 11 errors in 1 file (checked 2 source files) These ghosts are now haunting me: cpython/Tools/clinic/clinic.py Line 4314 in d65b783
I'm tempted to use a cpython/Tools/clinic/clinic.py Lines 4591 to 4593 in d65b783
Unless there's a more typing idiomatic way, I'm inclined to simply do something like this: diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index b0e5142efa..c730688fbf 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -4588,9 +4588,9 @@ def state_modulename_name(self, line: str | None) -> None:
line, _, returns = line.partition('->')
- full_name, _, c_basename = line.partition(' as ')
- full_name = full_name.strip()
- c_basename = c_basename.strip() or None
+ left, _, right = line.partition(' as ')
+ full_name = left.strip()
+ c_basename = right.strip() or None
if not is_legal_py_identifier(full_name):
fail("Illegal function name:", full_name) |
This error is because the code does something equivalent to this: condition: bool
if condition:
foo = "foo"
else:
foo = None Iff you don't have a type annotation for a variable, then a type checker will by default infer the most specific type possible for an assignment. That means that in the first branch, mypy infers that the type of The solution is to give an explicit type annotation before the variable has been assigned in either branch, to force mypy to infer a broader type in both branches instead of inferring the most specific type possible: condition: bool
foo: str | None
if condition:
foo = "foo"
else:
foo = None |
I think mypy is flagging a real bug in the code here. The issue is this block of code here: cpython/Tools/clinic/clinic.py Lines 5033 to 5039 in d694f04
The function is annotated as return cpython/Tools/clinic/clinic.py Line 4617 in d694f04
The code as written, however, doesn't guarantee that all the keys in the |
Here's two possible ways of fixing this latent bug: (1) --- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -5048,6 +5048,7 @@ def parse_converter(annotation: ast.expr | None) -> tuple[str, bool, KwargDict]:
kwargs = {
node.arg: eval_ast_expr(node.value, symbols)
for node in annotation.keywords
+ if isinstance(node.arg, str)
}
return name, False, kwargs
case _: (2) --- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -5045,10 +5045,11 @@ def parse_converter(annotation: ast.expr | None) -> tuple[str, bool, KwargDict]:
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: dict[str, Any] = {}
+ for node in annotation.keywords:
+ if not isinstance(node.arg, str):
+ raise TypeError("Unexpectedly found a keyword node with a non-str arg!")
+ kwargs[node.arg] = eval_ast_expr(node.value, symbols)
return name, False, kwargs
case _:
fail( You tell me which would be more correct here (or if another solution would be better than either of these)! |
It looks to me like the annotation for the --- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -2448,7 +2448,7 @@ def __init__(
cls: Class | None = None,
c_basename: str | None = None,
full_name: str | None = None,
- return_converter: ReturnConverterType,
+ return_converter: "CReturnConverter",
return_annotation = inspect.Signature.empty,
docstring: str | None = None,
kind: str = CALLABLE, |
Good sleuthing! Let's fix bugs separately from adding annotations. I'll create an issue. EDIT: Created gh-106359 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, just two questions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
* main: (167 commits) pythongh-91053: make func watcher tests resilient to other func watchers (python#106286) pythongh-104050: Add more type hints to Argument Clinic DSLParser() (python#106354) pythongh-106359: Fix corner case bugs in Argument Clinic converter parser (python#106361) pythongh-104146: Remove unused attr 'parameter_indent' from clinic.DLParser (python#106358) pythongh-106320: Remove private _PyErr C API functions (python#106356) pythongh-104050: Annotate Argument Clinic DSLParser attributes (python#106357) pythongh-106320: Create pycore_modsupport.h header file (python#106355) pythongh-106320: Move _PyUnicodeWriter to the internal C API (python#106342) pythongh-61215: New mock to wait for multi-threaded events to happen (python#16094) Document PYTHONSAFEPATH along side -P (python#106122) Replace the esoteric term 'datum' when describing dict comprehensions (python#106119) pythongh-104050: Add more type hints to Argument Clinic DSLParser() (python#106343) pythongh-106320: _testcapi avoids private _PyUnicode_EqualToASCIIString() (python#106341) pythongh-106320: Add pycore_complexobject.h header file (python#106339) pythongh-106078: Move DecimalException to _decimal state (python#106301) pythongh-106320: Use _PyInterpreterState_GET() (python#106336) pythongh-106320: Remove private _PyInterpreterState functions (python#106335) pythongh-104922: Doc: add note about PY_SSIZE_T_CLEAN (python#106314) pythongh-106217: Truncate the issue body size of `new-bugs-announce-notifier` (python#106329) pythongh-104922: remove PY_SSIZE_T_CLEAN (python#106315) ...
Add basic annotations to state_modulename_name()