Skip to content

Commit 5f8c447

Browse files
ilevkivskyiJukkaL
authored andcommitted
meet.py should consider all Tuple[t1, t2, ...] as subtypes of plain tuple (#2465)
* Fix isinstance(Tuple[int, str], tuple) * Simplify tests by --hide-error-context * Make meet for tuples apply to all homogeneous tuples
1 parent d5410b1 commit 5f8c447

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

mypy/meet.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ def visit_instance(self, t: Instance) -> Type:
227227
return NoneTyp()
228228
elif isinstance(self.s, TypeType):
229229
return meet_types(t, self.s)
230+
elif isinstance(self.s, TupleType):
231+
return meet_types(t, self.s)
230232
else:
231233
return self.default(self.s)
232234

@@ -243,6 +245,10 @@ def visit_tuple_type(self, t: TupleType) -> Type:
243245
items.append(self.meet(t.items[i], self.s.items[i]))
244246
# TODO: What if the fallbacks are different?
245247
return TupleType(items, t.fallback)
248+
# meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>].
249+
elif (isinstance(self.s, Instance) and
250+
self.s.type.fullname() == 'builtins.tuple' and self.s.args):
251+
return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items])
246252
else:
247253
return self.default(self.s)
248254

test-data/unit/check-tuples.test

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,84 @@ b = (1, 'x')
808808
a = (0, *b, '')
809809
[builtins fixtures/tuple.pyi]
810810

811+
[case testTupleMeetTupleAny]
812+
# flags: --hide-error-context
813+
from typing import Union, Tuple
814+
class A: pass
815+
class B: pass
816+
817+
def f(x: Union[B, Tuple[A, A]]) -> None:
818+
if isinstance(x, tuple):
819+
reveal_type(x) # E: Revealed type is 'Tuple[__main__.A, __main__.A]'
820+
else:
821+
reveal_type(x) # E: Revealed type is '__main__.B'
822+
823+
def g(x: Union[str, Tuple[str, str]]) -> None:
824+
if isinstance(x, tuple):
825+
reveal_type(x) # E: Revealed type is 'Tuple[builtins.str, builtins.str]'
826+
else:
827+
reveal_type(x) # E: Revealed type is 'builtins.str'
828+
829+
[builtins fixtures/tuple.pyi]
830+
[out]
831+
832+
[case testTupleMeetTUpleAnyComplex]
833+
# flags: --hide-error-context
834+
from typing import Tuple, Union
835+
836+
Pair = Tuple[int, int]
837+
Variant = Union[int, Pair]
838+
def tuplify(v: Variant) -> None:
839+
reveal_type(v) # E: Revealed type is 'Union[builtins.int, Tuple[builtins.int, builtins.int]]'
840+
if not isinstance(v, tuple):
841+
reveal_type(v) # E: Revealed type is 'builtins.int'
842+
v = (v, v)
843+
reveal_type(v) # E: Revealed type is 'Tuple[builtins.int, builtins.int]'
844+
reveal_type(v) # E: Revealed type is 'Tuple[builtins.int, builtins.int]'
845+
reveal_type(v[0]) # E: Revealed type is 'builtins.int'
846+
847+
Pair2 = Tuple[int, str]
848+
Variant2 = Union[int, Pair2]
849+
def tuplify2(v: Variant2) -> None:
850+
if isinstance(v, tuple):
851+
reveal_type(v) # E: Revealed type is 'Tuple[builtins.int, builtins.str]'
852+
else:
853+
reveal_type(v) # E: Revealed type is 'builtins.int'
854+
[builtins fixtures/tuple.pyi]
855+
[out]
856+
857+
[case testTupleMeetTupleAnyAfter]
858+
# flags: --hide-error-context
859+
from typing import Tuple, Union
860+
861+
def good(blah: Union[Tuple[int, int], int]) -> None:
862+
reveal_type(blah) # E: Revealed type is 'Union[Tuple[builtins.int, builtins.int], builtins.int]'
863+
if isinstance(blah, tuple):
864+
reveal_type(blah) # E: Revealed type is 'Tuple[builtins.int, builtins.int]'
865+
reveal_type(blah) # E: Revealed type is 'Union[Tuple[builtins.int, builtins.int], builtins.int]'
866+
[builtins fixtures/tuple.pyi]
867+
[out]
868+
869+
[case testTupleMeetTupleVariable]
870+
from typing import Tuple, TypeVar, Generic, Union
871+
T = TypeVar('T')
872+
873+
class A: pass
874+
class B1(A): pass
875+
class B2(A): pass
876+
class C: pass
877+
878+
x = None # type: Tuple[A, ...]
879+
y = None # type: Tuple[Union[B1, C], Union[B2, C]]
880+
881+
def g(x: T) -> Tuple[T, T]:
882+
return (x, x)
883+
884+
z = 1
885+
x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "Tuple[B1, B2]"
886+
[builtins fixtures/tuple.pyi]
887+
[out]
888+
811889
[case testTupleWithUndersizedContext]
812890
a = ([1], 'x')
813891
a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "Tuple[List[int], str, int]", variable has type "Tuple[List[int], str]")

test-data/unit/fixtures/tuple.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ class str: pass # For convenience
2323
T = TypeVar('T')
2424

2525
class list(Sequence[T], Generic[T]): pass
26+
def isinstance(x: object, t: type) -> bool: pass
2627

2728
def sum(iterable: Iterable[T], start: T = None) -> T: pass

0 commit comments

Comments
 (0)