From 7d27f5c91e8fa493f45238d21fc0cce1a76bf632 Mon Sep 17 00:00:00 2001 From: liuyuhui Date: Fri, 15 May 2020 10:45:59 +0800 Subject: [PATCH 1/3] fix error that tuple with star expr --- mypy/checker.py | 12 ++++++++++-- test-data/unit/check-tuples.test | 7 +++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b01be788aaa4..d05b9c79353b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2483,8 +2483,16 @@ def check_assignment_to_multiple_lvalues(self, lvalues: List[Lvalue], rvalue: Ex # control in cases like: a, b = [int, str] where rhs would get # type List[object] - rvalues = rvalue.items - + rvalues = [] # type: List[Expression] + for rval in rvalue.items: + if isinstance(rval, StarExpr): + typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type) + if isinstance(typs, TupleType): + rvalues.extend([TempNode(typ) for typ in typs.items]) + else: + rvalues.append(TempNode(typs)) + else: + rvalues.append(rval) if self.check_rvalue_count_in_assignment(lvalues, len(rvalues), context): star_index = next((i for i, lv in enumerate(lvalues) if isinstance(lv, StarExpr)), len(lvalues)) diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 2ae61f885eae..406b3e03117c 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1421,3 +1421,10 @@ t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3 # E: Incompatible types in assignment (expression has type Tuple[int, int, ... <15 more items>], variable has type Tuple[int, int, ... <10 more items>]) [builtins fixtures/tuple.pyi] + +[case testTupleWithStarExpr] +from typing import Tuple +points = (1, 2) # type: Tuple[int, int] +x, y, z = *points, 0 + +[builtins fixtures/tuple.pyi] From fcd585576d4ba6dc9804cabf49f98cc6a147c70c Mon Sep 17 00:00:00 2001 From: liuyuhui Date: Tue, 26 May 2020 13:12:28 +0800 Subject: [PATCH 2/3] add support for iterable --- mypy/checker.py | 24 ++++++++++++++++++++++-- test-data/unit/check-tuples.test | 22 ++++++++++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index d05b9c79353b..1ed38ced4a20 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2482,17 +2482,37 @@ def check_assignment_to_multiple_lvalues(self, lvalues: List[Lvalue], rvalue: Ex # using the type of rhs, because this allowed more fine grained # control in cases like: a, b = [int, str] where rhs would get # type List[object] - rvalues = [] # type: List[Expression] + lhs_len = len(lvalues) + idx_of_iterable = [] + item_type_of_iterable = [] # type: List['mypy.types.Type'] + idx = 0 for rval in rvalue.items: if isinstance(rval, StarExpr): typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type) if isinstance(typs, TupleType): rvalues.extend([TempNode(typ) for typ in typs.items]) + lhs_len -= len(typs.items) + idx += len(typs.items) + elif self.type_is_iterable(typs) and isinstance(typs, Instance): + item_type_of_iterable.append(self.iterable_item_type(typs)) + idx_of_iterable.append(idx) else: - rvalues.append(TempNode(typs)) + self.fail("StarExpr should not be a '{}'".format(typs), context) else: rvalues.append(rval) + lhs_len -= 1 + idx += 1 + num_every_iterable = 0 + num_last_iterable = 0 + if len(idx_of_iterable): + num_every_iterable = int(lhs_len / len(idx_of_iterable)) + num_last_iterable = lhs_len - (len(idx_of_iterable) - 1) * int(num_every_iterable) + for i, (idx, item_type) in enumerate(zip(idx_of_iterable, item_type_of_iterable)): + if i == (len(idx_of_iterable) - 1): + rvalues[idx:idx] = [TempNode(item_type) for _ in range(num_last_iterable)] + else: + rvalues[idx:idx] = [TempNode(item_type) for _ in range(num_every_iterable)] if self.check_rvalue_count_in_assignment(lvalues, len(rvalues), context): star_index = next((i for i, lv in enumerate(lvalues) if isinstance(lv, StarExpr)), len(lvalues)) diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 406b3e03117c..9c39803a721f 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1423,8 +1423,26 @@ t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3 [builtins fixtures/tuple.pyi] [case testTupleWithStarExpr] -from typing import Tuple -points = (1, 2) # type: Tuple[int, int] +from typing import Tuple, List +points = (1, "test") # type: Tuple[int, str] x, y, z = *points, 0 +reveal_type(x) # N: Revealed type is 'builtins.int' +reveal_type(y) # N: Revealed type is 'builtins.str' +reveal_type(z) # N: Revealed type is 'builtins.int' + +points2 = [1,2] +x2, y2, z2= *points2, "test" + +reveal_type(x2) # N: Revealed type is 'builtins.int*' +reveal_type(y2) # N: Revealed type is 'builtins.int*' +reveal_type(z2) # N: Revealed type is 'builtins.str' + +x3, x4, y3, y4, z3 = *points, *points2, "test" + +reveal_type(x3) # N: Revealed type is 'builtins.int' +reveal_type(x4) # N: Revealed type is 'builtins.str' +reveal_type(y3) # N: Revealed type is 'builtins.int*' +reveal_type(y4) # N: Revealed type is 'builtins.int*' +reveal_type(z3) # N: Revealed type is 'builtins.str' [builtins fixtures/tuple.pyi] From f884bc8c21d33fee30ec8e6a4dfc4a957932c2fc Mon Sep 17 00:00:00 2001 From: LiuYuHui Date: Sun, 28 Jun 2020 11:16:32 +0800 Subject: [PATCH 3/3] fix some potential errors and add more unit test --- mypy/checker.py | 55 +++++++++++++++++++------------- test-data/unit/check-tuples.test | 12 +++++++ 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 1ed38ced4a20..2558440168e3 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2483,36 +2483,47 @@ def check_assignment_to_multiple_lvalues(self, lvalues: List[Lvalue], rvalue: Ex # control in cases like: a, b = [int, str] where rhs would get # type List[object] rvalues = [] # type: List[Expression] - lhs_len = len(lvalues) - idx_of_iterable = [] - item_type_of_iterable = [] # type: List['mypy.types.Type'] - idx = 0 - for rval in rvalue.items: + iterable_type = None # type: Optional[Type] + last_idx = None # type: Optional[int] + for idx_rval, rval in enumerate(rvalue.items): if isinstance(rval, StarExpr): typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type) if isinstance(typs, TupleType): rvalues.extend([TempNode(typ) for typ in typs.items]) - lhs_len -= len(typs.items) - idx += len(typs.items) elif self.type_is_iterable(typs) and isinstance(typs, Instance): - item_type_of_iterable.append(self.iterable_item_type(typs)) - idx_of_iterable.append(idx) + if (iterable_type is not None + and iterable_type != self.iterable_item_type(typs)): + self.fail("Contiguous iterable with same type expected", context) + else: + if last_idx is None or last_idx + 1 == idx_rval: + rvalues.append(rval) + last_idx = idx_rval + iterable_type = self.iterable_item_type(typs) + else: + self.fail("Contiguous iterable with same type expected", context) else: - self.fail("StarExpr should not be a '{}'".format(typs), context) + self.fail("Invalid type '{}' for *expr (iterable expected)".format(typs), + context) else: rvalues.append(rval) - lhs_len -= 1 - idx += 1 - num_every_iterable = 0 - num_last_iterable = 0 - if len(idx_of_iterable): - num_every_iterable = int(lhs_len / len(idx_of_iterable)) - num_last_iterable = lhs_len - (len(idx_of_iterable) - 1) * int(num_every_iterable) - for i, (idx, item_type) in enumerate(zip(idx_of_iterable, item_type_of_iterable)): - if i == (len(idx_of_iterable) - 1): - rvalues[idx:idx] = [TempNode(item_type) for _ in range(num_last_iterable)] - else: - rvalues[idx:idx] = [TempNode(item_type) for _ in range(num_every_iterable)] + iterable_start = None # type: Optional[int] + iterable_end = None # type: Optional[int] + for i, rval in enumerate(rvalues): + if isinstance(rval, StarExpr): + typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type) + if self.type_is_iterable(typs) and isinstance(typs, Instance): + if iterable_start is None: + iterable_start = i + iterable_end = i + if (iterable_start is not None + and iterable_end is not None + and iterable_type is not None): + iterable_num = iterable_end - iterable_start + 1 + rvalue_needed = len(lvalues) - (len(rvalues) - iterable_num) + if rvalue_needed > 0: + rvalues = rvalues[0: iterable_start] + [TempNode(iterable_type) + for i in range(rvalue_needed)] + rvalues[iterable_end + 1:] + if self.check_rvalue_count_in_assignment(lvalues, len(rvalues), context): star_index = next((i for i, lv in enumerate(lvalues) if isinstance(lv, StarExpr)), len(lvalues)) diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 9c39803a721f..1c4b537325e8 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1445,4 +1445,16 @@ reveal_type(y3) # N: Revealed type is 'builtins.int*' reveal_type(y4) # N: Revealed type is 'builtins.int*' reveal_type(z3) # N: Revealed type is 'builtins.str' +x5, x6, y5, y6, z4 = *points2, *points2, "test" + +reveal_type(x5) # N: Revealed type is 'builtins.int*' +reveal_type(x6) # N: Revealed type is 'builtins.int*' +reveal_type(y5) # N: Revealed type is 'builtins.int*' +reveal_type(y6) # N: Revealed type is 'builtins.int*' +reveal_type(z4) # N: Revealed type is 'builtins.str' + +points3 = ["test1", "test2"] +x7, x8, y7, y8 = *points2, *points3 # E: Contiguous iterable with same type expected + +x9, y9, x10, y10, z5 = *points2, 1, *points2 # E: Contiguous iterable with same type expected [builtins fixtures/tuple.pyi]