From d4c4c2263e78f3d16656382227aeea73c29e1c80 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 16 Nov 2016 16:51:33 +0100 Subject: [PATCH 1/3] Fix isinstance(Tuple[int, str], tuple) --- mypy/meet.py | 6 +++ test-data/unit/check-tuples.test | 67 +++++++++++++++++++++++++++++++ test-data/unit/fixtures/tuple.pyi | 1 + 3 files changed, 74 insertions(+) diff --git a/mypy/meet.py b/mypy/meet.py index 18796ae2491cb..f078e345d37d4 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -227,6 +227,8 @@ def visit_instance(self, t: Instance) -> Type: return NoneTyp() elif isinstance(self.s, TypeType): return meet_types(t, self.s) + elif isinstance(self.s, TupleType): + return meet_types(t, self.s) else: return self.default(self.s) @@ -243,6 +245,10 @@ def visit_tuple_type(self, t: TupleType) -> Type: items.append(self.meet(t.items[i], self.s.items[i])) # TODO: What if the fallbacks are different? return TupleType(items, t.fallback) + # Special case: all Tuple[t1, t2, ...] are subtypes of plain tuple. + elif (isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.tuple' + and len(self.s.args) == 1 and isinstance(self.s.args[0], AnyType)): + return t else: return self.default(self.s) diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index bdd3805f6c249..e97ddcb904246 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -808,6 +808,73 @@ b = (1, 'x') a = (0, *b, '') [builtins fixtures/tuple.pyi] +[case testTupleMeetTupleAny] +from typing import Union, Tuple +class A: pass +class B: pass + +def f(x: Union[B, Tuple[A, A]]) -> None: + if isinstance(x, tuple): + reveal_type(x) + else: + reveal_type(x) + +def g(x: Union[str, Tuple[str, str]]) -> None: + if isinstance(x, tuple): + reveal_type(x) # E: Revealed type is 'Tuple[builtins.str, builtins.str]' + else: + reveal_type(x) # E: Revealed type is 'builtins.str' + +[builtins fixtures/tuple.pyi] +[out] +main: note: In function "f": +main:7: error: Revealed type is 'Tuple[__main__.A, __main__.A]' +main:9: error: Revealed type is '__main__.B' +main: note: In function "g": + +[case testTupleMeetTUpleAnyComplex] +from typing import Tuple, Union + +Pair = Tuple[int, int] +Variant = Union[int, Pair] +def tuplify(v: Variant) -> None: + reveal_type(v) + if not isinstance(v, tuple): + reveal_type(v) + v = (v, v) + reveal_type(v) + reveal_type(v) + reveal_type(v[0]) + +Pair2 = Tuple[int, str] +Variant2 = Union[int, Pair2] +def tuplify2(v: Variant2) -> None: + if isinstance(v, tuple): + reveal_type(v) # E: Revealed type is 'Tuple[builtins.int, builtins.str]' + else: + reveal_type(v) # E: Revealed type is 'builtins.int' +[builtins fixtures/tuple.pyi] +[out] +main: note: In function "tuplify": +main:6: error: Revealed type is 'Union[builtins.int, Tuple[builtins.int, builtins.int]]' +main:8: error: Revealed type is 'builtins.int' +main:10: error: Revealed type is 'Tuple[builtins.int, builtins.int]' +main:11: error: Revealed type is 'Tuple[builtins.int, builtins.int]' +main:12: error: Revealed type is 'builtins.int' +main: note: In function "tuplify2": + +[case testTupleMeetTupleAnyAfter] +from typing import Tuple, Union + +def good(blah: Union[Tuple[int, int], int]) -> None: + reveal_type(blah) # E: Revealed type is 'Union[Tuple[builtins.int, builtins.int], builtins.int]' + if isinstance(blah, tuple): + reveal_type(blah) # E: Revealed type is 'Tuple[builtins.int, builtins.int]' + reveal_type(blah) # E: Revealed type is 'Union[Tuple[builtins.int, builtins.int], builtins.int]' +[builtins fixtures/tuple.pyi] +[out] +main: note: In function "good": + [case testTupleWithUndersizedContext] a = ([1], 'x') a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "Tuple[List[int], str, int]", variable has type "Tuple[List[int], str]") diff --git a/test-data/unit/fixtures/tuple.pyi b/test-data/unit/fixtures/tuple.pyi index f6d36cc0d2dfa..6fe63b6ecd541 100644 --- a/test-data/unit/fixtures/tuple.pyi +++ b/test-data/unit/fixtures/tuple.pyi @@ -23,5 +23,6 @@ class str: pass # For convenience T = TypeVar('T') class list(Sequence[T], Generic[T]): pass +def isinstance(x: object, t: type) -> bool: pass def sum(iterable: Iterable[T], start: T = None) -> T: pass From bc1b89e6b9e70e504ce0df4e2af4ebb16e877625 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 22 Nov 2016 14:14:00 +0100 Subject: [PATCH 2/3] Simplify tests by --hide-error-context --- test-data/unit/check-tuples.test | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index e97ddcb904246..49a54f3a0c542 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -809,15 +809,16 @@ a = (0, *b, '') [builtins fixtures/tuple.pyi] [case testTupleMeetTupleAny] +# flags: --hide-error-context from typing import Union, Tuple class A: pass class B: pass def f(x: Union[B, Tuple[A, A]]) -> None: if isinstance(x, tuple): - reveal_type(x) + reveal_type(x) # E: Revealed type is 'Tuple[__main__.A, __main__.A]' else: - reveal_type(x) + reveal_type(x) # E: Revealed type is '__main__.B' def g(x: Union[str, Tuple[str, str]]) -> None: if isinstance(x, tuple): @@ -827,24 +828,21 @@ def g(x: Union[str, Tuple[str, str]]) -> None: [builtins fixtures/tuple.pyi] [out] -main: note: In function "f": -main:7: error: Revealed type is 'Tuple[__main__.A, __main__.A]' -main:9: error: Revealed type is '__main__.B' -main: note: In function "g": [case testTupleMeetTUpleAnyComplex] +# flags: --hide-error-context from typing import Tuple, Union Pair = Tuple[int, int] Variant = Union[int, Pair] def tuplify(v: Variant) -> None: - reveal_type(v) + reveal_type(v) # E: Revealed type is 'Union[builtins.int, Tuple[builtins.int, builtins.int]]' if not isinstance(v, tuple): - reveal_type(v) + reveal_type(v) # E: Revealed type is 'builtins.int' v = (v, v) - reveal_type(v) - reveal_type(v) - reveal_type(v[0]) + reveal_type(v) # E: Revealed type is 'Tuple[builtins.int, builtins.int]' + reveal_type(v) # E: Revealed type is 'Tuple[builtins.int, builtins.int]' + reveal_type(v[0]) # E: Revealed type is 'builtins.int' Pair2 = Tuple[int, str] Variant2 = Union[int, Pair2] @@ -855,15 +853,9 @@ def tuplify2(v: Variant2) -> None: reveal_type(v) # E: Revealed type is 'builtins.int' [builtins fixtures/tuple.pyi] [out] -main: note: In function "tuplify": -main:6: error: Revealed type is 'Union[builtins.int, Tuple[builtins.int, builtins.int]]' -main:8: error: Revealed type is 'builtins.int' -main:10: error: Revealed type is 'Tuple[builtins.int, builtins.int]' -main:11: error: Revealed type is 'Tuple[builtins.int, builtins.int]' -main:12: error: Revealed type is 'builtins.int' -main: note: In function "tuplify2": [case testTupleMeetTupleAnyAfter] +# flags: --hide-error-context from typing import Tuple, Union def good(blah: Union[Tuple[int, int], int]) -> None: @@ -873,7 +865,6 @@ def good(blah: Union[Tuple[int, int], int]) -> None: reveal_type(blah) # E: Revealed type is 'Union[Tuple[builtins.int, builtins.int], builtins.int]' [builtins fixtures/tuple.pyi] [out] -main: note: In function "good": [case testTupleWithUndersizedContext] a = ([1], 'x') From bdd027e9043080ba2682b8276c25b0e2ecbe600d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 22 Nov 2016 15:29:07 +0100 Subject: [PATCH 3/3] Make meet for tuples apply to all homogeneous tuples --- mypy/meet.py | 8 ++++---- test-data/unit/check-tuples.test | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/mypy/meet.py b/mypy/meet.py index f078e345d37d4..7030538eec382 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -245,10 +245,10 @@ def visit_tuple_type(self, t: TupleType) -> Type: items.append(self.meet(t.items[i], self.s.items[i])) # TODO: What if the fallbacks are different? return TupleType(items, t.fallback) - # Special case: all Tuple[t1, t2, ...] are subtypes of plain tuple. - elif (isinstance(self.s, Instance) and self.s.type.fullname() == 'builtins.tuple' - and len(self.s.args) == 1 and isinstance(self.s.args[0], AnyType)): - return t + # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>]. + elif (isinstance(self.s, Instance) and + self.s.type.fullname() == 'builtins.tuple' and self.s.args): + return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items]) else: return self.default(self.s) diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 49a54f3a0c542..dea0a08d514cb 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -866,6 +866,26 @@ def good(blah: Union[Tuple[int, int], int]) -> None: [builtins fixtures/tuple.pyi] [out] +[case testTupleMeetTupleVariable] +from typing import Tuple, TypeVar, Generic, Union +T = TypeVar('T') + +class A: pass +class B1(A): pass +class B2(A): pass +class C: pass + +x = None # type: Tuple[A, ...] +y = None # type: Tuple[Union[B1, C], Union[B2, C]] + +def g(x: T) -> Tuple[T, T]: + return (x, x) + +z = 1 +x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "Tuple[B1, B2]" +[builtins fixtures/tuple.pyi] +[out] + [case testTupleWithUndersizedContext] a = ([1], 'x') a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "Tuple[List[int], str, int]", variable has type "Tuple[List[int], str]")