Skip to content

Commit 5a9d022

Browse files
authored
Fix internal error on list/dict comprehension with walrus operator in global scope (#9062)
* Add tests for dicts using assignment expression * Confirm the symbol table retrieved is not None It may be None in the case of a list comprehension being declared in a class scope (not in a function of a class). This, however, is not valid python syntax. * Add tests for assignment expression in class scope
1 parent fd99544 commit 5a9d022

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

mypy/semanal.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4542,9 +4542,18 @@ def current_symbol_table(self, escape_comprehensions: bool = False) -> SymbolTab
45424542
if self.is_func_scope():
45434543
assert self.locals[-1] is not None
45444544
if escape_comprehensions:
4545+
assert len(self.locals) == len(self.is_comprehension_stack)
4546+
# Retrieve the symbol table from the enclosing non-comprehension scope.
45454547
for i, is_comprehension in enumerate(reversed(self.is_comprehension_stack)):
45464548
if not is_comprehension:
4547-
names = self.locals[-1 - i]
4549+
if i == len(self.locals) - 1: # The last iteration.
4550+
# The caller of the comprehension is in the global space.
4551+
names = self.globals
4552+
else:
4553+
names_candidate = self.locals[-1 - i]
4554+
assert names_candidate is not None, \
4555+
"Escaping comprehension from invalid scope"
4556+
names = names_candidate
45484557
break
45494558
else:
45504559
assert False, "Should have at least one non-comprehension scope"

test-data/unit/check-python38.test

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,27 @@ if a := 2:
198198
while b := "x":
199199
reveal_type(b) # N: Revealed type is 'builtins.str'
200200

201+
l = [y2 := 1, y2 + 2, y2 + 3]
202+
reveal_type(y2) # N: Revealed type is 'builtins.int'
203+
reveal_type(l) # N: Revealed type is 'builtins.list[builtins.int*]'
204+
205+
filtered_data = [y3 for x in l if (y3 := a) is not None]
206+
reveal_type(filtered_data) # N: Revealed type is 'builtins.list[builtins.int*]'
207+
reveal_type(y3) # N: Revealed type is 'builtins.int'
208+
209+
d = {'a': (a2 := 1), 'b': a2 + 1, 'c': a2 + 2}
210+
reveal_type(d) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]'
211+
reveal_type(a2) # N: Revealed type is 'builtins.int'
212+
213+
d2 = {(prefix := 'key_') + 'a': (start_val := 1), prefix + 'b': start_val + 1, prefix + 'c': start_val + 2}
214+
reveal_type(d2) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]'
215+
reveal_type(prefix) # N: Revealed type is 'builtins.str'
216+
reveal_type(start_val) # N: Revealed type is 'builtins.int'
217+
218+
filtered_dict = {k: new_v for k, v in [('a', 1), ('b', 2), ('c', 3)] if (new_v := v + 1) == 2}
219+
reveal_type(filtered_dict) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]'
220+
reveal_type(new_v) # N: Revealed type is 'builtins.int'
221+
201222
def f(x: int = (c := 4)) -> int:
202223
if a := 2:
203224
reveal_type(a) # N: Revealed type is 'builtins.int'
@@ -218,6 +239,19 @@ def f(x: int = (c := 4)) -> int:
218239
reveal_type(filtered_data) # N: Revealed type is 'builtins.list[builtins.int*]'
219240
reveal_type(y3) # N: Revealed type is 'builtins.int'
220241

242+
d = {'a': (a2 := 1), 'b': a2 + 1, 'c': a2 + 2}
243+
reveal_type(d) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]'
244+
reveal_type(a2) # N: Revealed type is 'builtins.int'
245+
246+
d2 = {(prefix := 'key_') + 'a': (start_val := 1), prefix + 'b': start_val + 1, prefix + 'c': start_val + 2}
247+
reveal_type(d2) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]'
248+
reveal_type(prefix) # N: Revealed type is 'builtins.str'
249+
reveal_type(start_val) # N: Revealed type is 'builtins.int'
250+
251+
filtered_dict = {k: new_v for k, v in [('a', 1), ('b', 2), ('c', 3)] if (new_v := v + 1) == 2}
252+
reveal_type(filtered_dict) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]'
253+
reveal_type(new_v) # N: Revealed type is 'builtins.int'
254+
221255
# https://www.python.org/dev/peps/pep-0572/#exceptional-cases
222256
(y4 := 3)
223257
reveal_type(y4) # N: Revealed type is 'builtins.int'
@@ -304,6 +338,28 @@ def check_narrow(x: Optional[int], s: List[int]) -> None:
304338
if isinstance((y := x), int):
305339
reveal_type(y) # N: Revealed type is 'builtins.int'
306340

341+
class AssignmentExpressionsClass:
342+
x = (y := 1) + (z := 2)
343+
reveal_type(z) # N: Revealed type is 'builtins.int'
344+
345+
l = [x2 := 1, 2, 3]
346+
reveal_type(x2) # N: Revealed type is 'builtins.int'
347+
348+
def __init__(self) -> None:
349+
reveal_type(self.z) # N: Revealed type is 'builtins.int'
350+
351+
l = [z2 := 1, z2 + 2, z2 + 3]
352+
reveal_type(z2) # N: Revealed type is 'builtins.int'
353+
reveal_type(l) # N: Revealed type is 'builtins.list[builtins.int*]'
354+
355+
filtered_data = [z3 for x in l if (z3 := 1) is not None]
356+
reveal_type(filtered_data) # N: Revealed type is 'builtins.list[builtins.int*]'
357+
reveal_type(z3) # N: Revealed type is 'builtins.int'
358+
359+
# Assignment expressions from inside the class should not escape the class scope.
360+
reveal_type(x2) # E: Name 'x2' is not defined # N: Revealed type is 'Any'
361+
reveal_type(z2) # E: Name 'z2' is not defined # N: Revealed type is 'Any'
362+
307363
[builtins fixtures/isinstancelist.pyi]
308364

309365
[case testWalrusPartialTypes]

0 commit comments

Comments
 (0)