-
-
Notifications
You must be signed in to change notification settings - Fork 264
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
Improve inference for generic classes (PEP 695) #2433
Conversation
Codecov ReportAll modified and coverable lines are covered by tests β
Additional details and impacted files@@ Coverage Diff @@
## main #2433 +/- ##
==========================================
- Coverage 92.79% 92.79% -0.01%
==========================================
Files 94 94
Lines 11109 11118 +9
==========================================
+ Hits 10309 10317 +8
- Misses 800 801 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
|
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
astroid/brain/brain_typing.py
Outdated
) -> Iterator[ClassDef]: | ||
"""Add __class_getitem__ for generic classes. Python 3.12+.""" | ||
try: | ||
value = next(node.infer()) |
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.
Thanks for tackling this. One question for my own information: why do we need to use inference? Can we just check node.type_params
?
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.
Tbh I'm not quite sure either. All other methods do this as well. My best guess is that with the brain_**
we overwrite the default inference. However, since we still want that, we need to call node.infer()
ourselves.
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.
Interestingly, if I add assert node is value
, it passes.
Can we use this opportunity to explore avoiding inference here?
"""Hack. Return any Node so inference doesn't fail | ||
when evaluating __class_getitem__. Revert if it's causing issues. | ||
""" | ||
return | ||
yield | ||
yield nodes.Const(None) |
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.
This really isn't the "correct" solution but it works as the result is ignored anyway when inferring the __class_getitem__
call. It just can't raise an Exception - that would lead to Uninferable
.
The typing brain solves that by inferring the T = TypeVar(...)
calls as new classes which is better but probably also not fully right.
If it was actually uses, I believe we would've seen issue reports for it by now.
astroid/astroid/brain/brain_typing.py
Lines 139 to 144 in 16da308
typename = node.args[0].as_string().strip("'") | |
try: | |
node = extract_node(TYPING_TYPE_TEMPLATE.format(typename)) | |
except AstroidSyntaxError as exc: | |
raise InferenceError from exc | |
return node.infer(context=context_itton) |
astroid/astroid/brain/brain_typing.py
Lines 47 to 58 in 16da308
TYPING_TYPE_TEMPLATE = """ | |
class Meta(type): | |
def __getitem__(self, item): | |
return self | |
@property | |
def __args__(self): | |
return () | |
class {0}(metaclass=Meta): | |
pass | |
""" |
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.
Open to different ideas here, but I think this would be a nice opportunity to verify things still work if we don't use inference.
Should we guard this under if node.type_params: ? |
That is done by the predicate. No need to duplicate it I think def _looks_like_generic_class_pep695(node: ClassDef) -> bool:
"""Check if class is using type parameter. Python 3.12+."""
return len(node.type_params) > 0 |
Investigated that a bit more. With the previous setup, we would end up calling the inference_tip twice as the astroid/astroid/inference_tip.py Lines 45 to 49 in e43e045
|
(cherry picked from commit fadac92)
(cherry picked from commit fadac92) Co-authored-by: Marc Mueller <[email protected]>
Type of Changes
Description
Generic classes with PEP 695 should be considered subscriptable, i.e. have a
__class_getitem__
method.Refs pylint-dev/pylint#9406
Will close the issue after adding some more tests in pylint.