Skip to content

Commit 17b1f9c

Browse files
authored
Fix *expr in rvalue (#8827)
Fixes #7779.
1 parent a08bbbb commit 17b1f9c

File tree

2 files changed

+78
-2
lines changed

2 files changed

+78
-2
lines changed

mypy/checker.py

+41-2
Original file line numberDiff line numberDiff line change
@@ -2482,8 +2482,47 @@ def check_assignment_to_multiple_lvalues(self, lvalues: List[Lvalue], rvalue: Ex
24822482
# using the type of rhs, because this allowed more fine grained
24832483
# control in cases like: a, b = [int, str] where rhs would get
24842484
# type List[object]
2485-
2486-
rvalues = rvalue.items
2485+
rvalues = [] # type: List[Expression]
2486+
iterable_type = None # type: Optional[Type]
2487+
last_idx = None # type: Optional[int]
2488+
for idx_rval, rval in enumerate(rvalue.items):
2489+
if isinstance(rval, StarExpr):
2490+
typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type)
2491+
if isinstance(typs, TupleType):
2492+
rvalues.extend([TempNode(typ) for typ in typs.items])
2493+
elif self.type_is_iterable(typs) and isinstance(typs, Instance):
2494+
if (iterable_type is not None
2495+
and iterable_type != self.iterable_item_type(typs)):
2496+
self.fail("Contiguous iterable with same type expected", context)
2497+
else:
2498+
if last_idx is None or last_idx + 1 == idx_rval:
2499+
rvalues.append(rval)
2500+
last_idx = idx_rval
2501+
iterable_type = self.iterable_item_type(typs)
2502+
else:
2503+
self.fail("Contiguous iterable with same type expected", context)
2504+
else:
2505+
self.fail("Invalid type '{}' for *expr (iterable expected)".format(typs),
2506+
context)
2507+
else:
2508+
rvalues.append(rval)
2509+
iterable_start = None # type: Optional[int]
2510+
iterable_end = None # type: Optional[int]
2511+
for i, rval in enumerate(rvalues):
2512+
if isinstance(rval, StarExpr):
2513+
typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type)
2514+
if self.type_is_iterable(typs) and isinstance(typs, Instance):
2515+
if iterable_start is None:
2516+
iterable_start = i
2517+
iterable_end = i
2518+
if (iterable_start is not None
2519+
and iterable_end is not None
2520+
and iterable_type is not None):
2521+
iterable_num = iterable_end - iterable_start + 1
2522+
rvalue_needed = len(lvalues) - (len(rvalues) - iterable_num)
2523+
if rvalue_needed > 0:
2524+
rvalues = rvalues[0: iterable_start] + [TempNode(iterable_type)
2525+
for i in range(rvalue_needed)] + rvalues[iterable_end + 1:]
24872526

24882527
if self.check_rvalue_count_in_assignment(lvalues, len(rvalues), context):
24892528
star_index = next((i for i, lv in enumerate(lvalues) if

test-data/unit/check-tuples.test

+37
Original file line numberDiff line numberDiff line change
@@ -1421,3 +1421,40 @@ t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3
14211421
# E: Incompatible types in assignment (expression has type Tuple[int, int, ... <15 more items>], variable has type Tuple[int, int, ... <10 more items>])
14221422

14231423
[builtins fixtures/tuple.pyi]
1424+
1425+
[case testTupleWithStarExpr]
1426+
from typing import Tuple, List
1427+
points = (1, "test") # type: Tuple[int, str]
1428+
x, y, z = *points, 0
1429+
reveal_type(x) # N: Revealed type is 'builtins.int'
1430+
reveal_type(y) # N: Revealed type is 'builtins.str'
1431+
reveal_type(z) # N: Revealed type is 'builtins.int'
1432+
1433+
points2 = [1,2]
1434+
x2, y2, z2= *points2, "test"
1435+
1436+
reveal_type(x2) # N: Revealed type is 'builtins.int*'
1437+
reveal_type(y2) # N: Revealed type is 'builtins.int*'
1438+
reveal_type(z2) # N: Revealed type is 'builtins.str'
1439+
1440+
x3, x4, y3, y4, z3 = *points, *points2, "test"
1441+
1442+
reveal_type(x3) # N: Revealed type is 'builtins.int'
1443+
reveal_type(x4) # N: Revealed type is 'builtins.str'
1444+
reveal_type(y3) # N: Revealed type is 'builtins.int*'
1445+
reveal_type(y4) # N: Revealed type is 'builtins.int*'
1446+
reveal_type(z3) # N: Revealed type is 'builtins.str'
1447+
1448+
x5, x6, y5, y6, z4 = *points2, *points2, "test"
1449+
1450+
reveal_type(x5) # N: Revealed type is 'builtins.int*'
1451+
reveal_type(x6) # N: Revealed type is 'builtins.int*'
1452+
reveal_type(y5) # N: Revealed type is 'builtins.int*'
1453+
reveal_type(y6) # N: Revealed type is 'builtins.int*'
1454+
reveal_type(z4) # N: Revealed type is 'builtins.str'
1455+
1456+
points3 = ["test1", "test2"]
1457+
x7, x8, y7, y8 = *points2, *points3 # E: Contiguous iterable with same type expected
1458+
1459+
x9, y9, x10, y10, z5 = *points2, 1, *points2 # E: Contiguous iterable with same type expected
1460+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)