Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-104050: Add more type hints to Argument Clinic DSLParser()#106354

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

erlend-aasland
Copy link
Contributor

@erlend-aaslanderlend-aasland commentedJul 3, 2023
edited by bedevere-bot
Loading

Add basic annotations to state_modulename_name()

Add basic annotations to state_modulename_name()
@erlend-aasland
Copy link
ContributorAuthor

erlend-aasland commentedJul 3, 2023
edited
Loading

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:

self.function=None

I'm tempted to use aFunction sentinel value, or just rewrite this passage. Perhaps there is a more idiomatic way in the typing world.

full_name,_,c_basename=line.partition(' as ')
full_name=full_name.strip()
c_basename=c_basename.strip()orNone

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.pyindex 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)

@AlexWaygood
Copy link
Member

Tools/clinic/clinic.py:4593: error: Incompatible types in assignment(expression has type "str | None", variable has type "str")  [assignment]            c_basename = right.strip() or None

This error is because the code does something equivalent to this:

condition:boolifcondition: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 offoo isstr, and in the second branch, it infers that the type offoo isNone. It then realises that it's inferring two different types for the two different branches, and squawks at you, as in any given function, any given variable should have the same type across the whole function.

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:boolfoo:str|Noneifcondition:foo="foo"else:foo=None
erlend-aasland reacted with thumbs up emoji

@AlexWaygood
Copy link
Member

Tools/clinic/clinic.py:5039: error: Incompatible return value type (got"tuple[str, bool, dict[str | None, Any]]", expected"tuple[str, bool, dict[str, Any]]")  [return-value]                    return name, False, kwargs                           ^~~~~~~~~~~~~~~~~~~

I think mypy is flagging a real bug in the code here. The issue is this block of code here:

caseast.Call(func=ast.Name(name)):
symbols=globals()
kwargs= {
node.arg:eval_ast_expr(node.value,symbols)
fornodeinannotation.keywords
}
returnname,False,kwargs

The function is annotated as returntuple[str, bool, KwargDict], andKwargDict is a type alias fordict[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:

return_converter=return_converters[name](**kwargs)

The code as written, however, doesn't guarantee that all the keys in thekwargs dictionary will be strings. In the dictionary comprehension, we can see thatannotation is anast.Call instance. That means thatannotation.keywords is of typelist[ast.keyword] -- we can see this from typeshed's stubs for theast 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. Ifannotation.keywords is of typelist[ast.keyword], that means that thenode variable in the dictionary comprehension is of typekeyword, which means (again using typeshed'sast stubs), thatnode.arg is of typestr | 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!

erlend-aasland reacted with rocket emoji

@AlexWaygood
Copy link
Member

AlexWaygood commentedJul 3, 2023
edited
Loading

AKA, the keys in this dictionary are not always guaranteed to be strings -- there's a latent bug in this code!

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)!

@AlexWaygood
Copy link
Member

Tools/clinic/clinic.py:4659: error: Argument "return_converter" to "Function"has incompatible type "CReturnConverter"; expected"Callable[..., CReturnConverter]"  [arg-type]    ...                                return_converter=return_converter, kin...                                                        ^~~~~~~~~~~~~~~~

It looks to me like the annotation for thereturn_converter attribute inFunction.__init__ is just incorrect. The following change should fix this error:

--- 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,
erlend-aasland reacted with thumbs up emoji

@erlend-aasland
Copy link
ContributorAuthor

erlend-aasland commentedJul 3, 2023
edited
Loading

[...] there's a latent bug in this code!

Good sleuthing! Let's fix bugs separately from adding annotations. I'll create an issue.

EDIT: Createdgh-106359

AlexWaygood reacted with thumbs up emoji

@erlend-aaslanderlend-aasland marked this pull request as ready for reviewJuly 3, 2023 13:18
Copy link
Member

@AlexWaygoodAlexWaygood left a 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

Copy link
Member

@AlexWaygoodAlexWaygood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

LGTM!

erlend-aasland reacted with hooray emoji
@erlend-aaslanderlend-aaslandenabled auto-merge (squash)July 3, 2023 13:58
@erlend-aaslanderlend-aasland merged commit2e2daac intopython:mainJul 3, 2023
carljm added a commit to carljm/cpython that referenced this pull requestJul 3, 2023
* 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)  ...
@erlend-aaslanderlend-aasland deleted the clinic/even-more-typing branchJuly 4, 2023 22:45
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@AlexWaygoodAlexWaygoodAlexWaygood approved these changes

Assignees
No one assigned
Labels
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

3 participants
@erlend-aasland@AlexWaygood@bedevere-bot

[8]ページ先頭

©2009-2025 Movatter.jp