-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Fix internal error on list/dict comprehension with walrus operator in global scope #9062
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
Changes from 4 commits
e66891f
c0f633f
822813d
ed9ba4f
a68bd68
8585a4f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -198,6 +198,27 @@ if a := 2: | |
while b := "x": | ||
reveal_type(b) # N: Revealed type is 'builtins.str' | ||
|
||
l = [y2 := 1, y2 + 2, y2 + 3] | ||
reveal_type(y2) # N: Revealed type is 'builtins.int' | ||
reveal_type(l) # N: Revealed type is 'builtins.list[builtins.int*]' | ||
|
||
filtered_data = [y3 for x in l if (y3 := a) is not None] | ||
reveal_type(filtered_data) # N: Revealed type is 'builtins.list[builtins.int*]' | ||
reveal_type(y3) # N: Revealed type is 'builtins.int' | ||
|
||
d = {'a': (a2 := 1), 'b': a2 + 1, 'c': a2 + 2} | ||
reveal_type(d) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' | ||
reveal_type(a2) # N: Revealed type is 'builtins.int' | ||
|
||
d2 = {(prefix := 'key_') + 'a': (start_val := 1), prefix + 'b': start_val + 1, prefix + 'c': start_val + 2} | ||
reveal_type(d2) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' | ||
reveal_type(prefix) # N: Revealed type is 'builtins.str' | ||
reveal_type(start_val) # N: Revealed type is 'builtins.int' | ||
|
||
filtered_dict = {k: new_v for k, v in [('a', 1), ('b', 2), ('c', 3)] if (new_v := v + 1) == 2} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If reviewers are wondering why I didn't just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is almost certainly because the test fixture you're using happens not to define |
||
reveal_type(filtered_dict) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' | ||
reveal_type(new_v) # N: Revealed type is 'builtins.int' | ||
|
||
def f(x: int = (c := 4)) -> int: | ||
if a := 2: | ||
reveal_type(a) # N: Revealed type is 'builtins.int' | ||
|
@@ -218,6 +239,19 @@ def f(x: int = (c := 4)) -> int: | |
reveal_type(filtered_data) # N: Revealed type is 'builtins.list[builtins.int*]' | ||
reveal_type(y3) # N: Revealed type is 'builtins.int' | ||
|
||
d = {'a': (a2 := 1), 'b': a2 + 1, 'c': a2 + 2} | ||
reveal_type(d) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' | ||
reveal_type(a2) # N: Revealed type is 'builtins.int' | ||
|
||
d2 = {(prefix := 'key_') + 'a': (start_val := 1), prefix + 'b': start_val + 1, prefix + 'c': start_val + 2} | ||
reveal_type(d2) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' | ||
reveal_type(prefix) # N: Revealed type is 'builtins.str' | ||
reveal_type(start_val) # N: Revealed type is 'builtins.int' | ||
|
||
filtered_dict = {k: new_v for k, v in [('a', 1), ('b', 2), ('c', 3)] if (new_v := v + 1) == 2} | ||
reveal_type(filtered_dict) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' | ||
reveal_type(new_v) # N: Revealed type is 'builtins.int' | ||
|
||
# https://www.python.org/dev/peps/pep-0572/#exceptional-cases | ||
(y4 := 3) | ||
reveal_type(y4) # N: Revealed type is 'builtins.int' | ||
|
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.
Why is this
cast()
needed? Could you use a# type:
comment on the first assignment instead?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.
the cast here was because
self.locals
is a list ofOptional[SymbolTable]
, and so without it, you would get:I reasoned (at the time) that it is a safe cast as long as we are not retrieving the
None
that is the first element in the list.mypy/mypy/semanal.py
Line 226 in ed9ba4f
However, now that you have mentioned to check the class scope, I have realised that there may be other elements in the list that are None:
mypy/mypy/semanal.py
Line 1166 in ed9ba4f
Perhaps once I deal with the class case properly this cast will not be necessary. Either way, for now, my original assumption that only the first entry is None seems invalid! So I will find an alternative approach.
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.
Since comprehensions in a class scope e.g.
can also result in the retrieved
self.locals[-1 -i]
symbol table being None, I added extra logic for that case, which removed the need for the cast, in a68bd68.Because this specific case is called out in https://www.python.org/dev/peps/pep-0572/#scope-of-the-target as being invalid syntax (I know that you know this, I am just being verbose), the specific logic that's added is actually just an assertion that we don't end up in that case.