-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Allow subscripted aliases at function scope #4000
Conversation
…i/mypy into fix-qualified-aliases
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 the PR! Looks pretty good, but I was confused in a few places.
"""Check if 'rvalue' represents a valid type allowed for aliasing | ||
(e.g. not a type variable). If yes, return the corresponding type and a list of | ||
qualified type variable names for generic aliases. | ||
If 'allow_unnormalized' is True, allow types like builtins.list[T]. |
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.
Does this PR also affect whether list[x]
is accepted in stubs?
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.
No, I just noticed that this parameter is unused in the function body when I added the new one.
@@ -650,7 +650,7 @@ def analyze_type_type_callee(self, item: Type, context: Context) -> Type: | |||
res = type_object_type(item.type, self.named_type) | |||
if isinstance(res, CallableType): | |||
res = res.copy_modified(from_type_type=True) | |||
return res | |||
return expand_type_by_instance(res, item) |
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.
What's this change?
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 is not directly related to this PR, but this was exposed by a test I updated. For example:
x: Type[C[int]]
x() # this should be 'C[int]', not 'C[<nothing>]'
y: Type[List]
y() # this should be 'List[Any]', not 'List[<nothing>]'
By the way, this use case is not mentioned in PEP 484, but there are tests for this, for example testClassValuedAttributesGeneric
. There are of course subtleties related to Type[...]
, but I think this change doesn't add unsafety, but removes some false positives.
mypy/semanal.py
Outdated
if (len(s.lvalues) == 1 and not self.is_func_scope() and | ||
not (self.type and isinstance(s.rvalue, NameExpr) and lvalue.is_def) | ||
if (len(s.lvalues) == 1 and | ||
not ((self.type or self.is_func_scope()) and |
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 condition is hard to understand. Add a comment describing what it does at a high level and maybe also give some concrete examples. It's also worth trying to simplify the nested boolean expression. Ideas:
- Make the top-level expression of form
e1 and (...) and not e2
instead ofe1 and not (...) and not e2
-- the firstnot
operator makes this a bit harder to understand, I think. - Use a temp variable to hold the result of
self.type or self.is_func_scope()
and give it a semantically meaningful name.
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.
OK, fixed. I replaced this with three if
s and thus saved one nesting level for the rest of the body.
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.
@JukkaL Thanks for review!
@@ -650,7 +650,7 @@ def analyze_type_type_callee(self, item: Type, context: Context) -> Type: | |||
res = type_object_type(item.type, self.named_type) | |||
if isinstance(res, CallableType): | |||
res = res.copy_modified(from_type_type=True) | |||
return res | |||
return expand_type_by_instance(res, item) |
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 is not directly related to this PR, but this was exposed by a test I updated. For example:
x: Type[C[int]]
x() # this should be 'C[int]', not 'C[<nothing>]'
y: Type[List]
y() # this should be 'List[Any]', not 'List[<nothing>]'
By the way, this use case is not mentioned in PEP 484, but there are tests for this, for example testClassValuedAttributesGeneric
. There are of course subtleties related to Type[...]
, but I think this change doesn't add unsafety, but removes some false positives.
"""Check if 'rvalue' represents a valid type allowed for aliasing | ||
(e.g. not a type variable). If yes, return the corresponding type and a list of | ||
qualified type variable names for generic aliases. | ||
If 'allow_unnormalized' is True, allow types like builtins.list[T]. |
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.
No, I just noticed that this parameter is unused in the function body when I added the new one.
mypy/semanal.py
Outdated
if (len(s.lvalues) == 1 and not self.is_func_scope() and | ||
not (self.type and isinstance(s.rvalue, NameExpr) and lvalue.is_def) | ||
if (len(s.lvalues) == 1 and | ||
not ((self.type or self.is_func_scope()) and |
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.
OK, fixed. I replaced this with three if
s and thus saved one nesting level for the rest of the body.
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 the updates! The code is now much easier to read.
Fixes #3145
This PR allows subscripted type aliases at function scope, as discussed in #3145 this will simplify the rules and have other pluses (apart from fixing the actual issue).
In addition, I prohibit reuse of bound type variables to define generic aliases, situations like this were always ambiguous:
Also prohibiting this use is consistent with how we prohibit bound type variable reuse for nested classes, one can always just use another type variable, so this works: