Skip to content

Commit 2c2d126

Browse files
authored
Fix tuple[Any, ...] subtyping (#16108)
Follow up to #16073 and #16076 Fix needed for https://github.com/python/mypy/pull/16053/files#r1316481395 I add test cases that would have caught my previous incorrect PR. I add an explicit case for the new desirable behaviour we see with zip.
1 parent f41e24c commit 2c2d126

File tree

3 files changed

+160
-12
lines changed

3 files changed

+160
-12
lines changed

mypy/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1359,7 +1359,7 @@ def set_strict_flags() -> None:
13591359
parser.error("Can only find occurrences of class members.")
13601360
if len(_find_occurrences) != 2:
13611361
parser.error("Can only find occurrences of non-nested class members.")
1362-
state.find_occurrences = _find_occurrences # type: ignore[assignment]
1362+
state.find_occurrences = _find_occurrences
13631363

13641364
# Set reports.
13651365
for flag, val in vars(special_opts).items():

mypy/subtypes.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,8 +463,10 @@ def visit_instance(self, left: Instance) -> bool:
463463
assert unpacked.type.fullname == "builtins.tuple"
464464
if isinstance(get_proper_type(unpacked.args[0]), AnyType):
465465
return not self.proper_subtype
466-
# TODO: we need a special case similar to above to consider (something that maps to)
467-
# tuple[Any, ...] a subtype of Tuple[<whatever>].
466+
if mapped.type.fullname == "builtins.tuple" and isinstance(
467+
get_proper_type(mapped.args[0]), AnyType
468+
):
469+
return not self.proper_subtype
468470
return False
469471
if isinstance(right, TypeVarTupleType):
470472
# tuple[Any, ...] is like Any in the world of tuples (see special case above).

test-data/unit/check-tuples.test

Lines changed: 155 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,19 +107,147 @@ class A: pass
107107
class B(A): pass
108108
[builtins fixtures/tuple.pyi]
109109

110-
[case testSubtypingWithNamedTupleType]
111-
from typing import Tuple
112-
t1: Tuple[A, A]
113-
t2: tuple
114-
115-
if int():
116-
t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[Any, ...]", variable has type "Tuple[A, A]")
117-
if int():
118-
t2 = t1
110+
[case testSubtypingWithTupleType]
111+
from __future__ import annotations
112+
from typing import Any, Tuple
113+
114+
tuple_aa: tuple[A, A]
115+
Tuple_aa: Tuple[A, A]
116+
117+
tuple_obj: tuple[object, ...]
118+
Tuple_obj: Tuple[object, ...]
119+
120+
tuple_obj_one: tuple[object]
121+
Tuple_obj_one: Tuple[object]
122+
123+
tuple_obj_two: tuple[object, object]
124+
Tuple_obj_two: Tuple[object, object]
125+
126+
tuple_any_implicit: tuple
127+
Tuple_any_implicit: Tuple
128+
129+
tuple_any: tuple[Any, ...]
130+
Tuple_any: Tuple[Any, ...]
131+
132+
tuple_any_one: tuple[Any]
133+
Tuple_any_one: Tuple[Any]
134+
135+
tuple_any_two: tuple[Any, Any]
136+
Tuple_any_two: Tuple[Any, Any]
137+
138+
def takes_tuple_aa(t: tuple[A, A]): ...
139+
140+
takes_tuple_aa(tuple_aa)
141+
takes_tuple_aa(Tuple_aa)
142+
takes_tuple_aa(tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]"
143+
takes_tuple_aa(Tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]"
144+
takes_tuple_aa(tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]"
145+
takes_tuple_aa(Tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]"
146+
takes_tuple_aa(tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]"
147+
takes_tuple_aa(Tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]"
148+
takes_tuple_aa(tuple_any_implicit)
149+
takes_tuple_aa(Tuple_any_implicit)
150+
takes_tuple_aa(tuple_any)
151+
takes_tuple_aa(Tuple_any)
152+
takes_tuple_aa(tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]"
153+
takes_tuple_aa(Tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]"
154+
takes_tuple_aa(tuple_any_two)
155+
takes_tuple_aa(Tuple_any_two)
156+
157+
def takes_tuple_any_implicit(t: tuple): ...
158+
159+
takes_tuple_any_implicit(tuple_aa)
160+
takes_tuple_any_implicit(Tuple_aa)
161+
takes_tuple_any_implicit(tuple_obj)
162+
takes_tuple_any_implicit(Tuple_obj)
163+
takes_tuple_any_implicit(tuple_obj_one)
164+
takes_tuple_any_implicit(Tuple_obj_one)
165+
takes_tuple_any_implicit(tuple_obj_two)
166+
takes_tuple_any_implicit(Tuple_obj_two)
167+
takes_tuple_any_implicit(tuple_any_implicit)
168+
takes_tuple_any_implicit(Tuple_any_implicit)
169+
takes_tuple_any_implicit(tuple_any)
170+
takes_tuple_any_implicit(Tuple_any)
171+
takes_tuple_any_implicit(tuple_any_one)
172+
takes_tuple_any_implicit(Tuple_any_one)
173+
takes_tuple_any_implicit(tuple_any_two)
174+
takes_tuple_any_implicit(Tuple_any_two)
175+
176+
def takes_tuple_any_one(t: tuple[Any]): ...
177+
178+
takes_tuple_any_one(tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]"
179+
takes_tuple_any_one(Tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]"
180+
takes_tuple_any_one(tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]"
181+
takes_tuple_any_one(Tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]"
182+
takes_tuple_any_one(tuple_obj_one)
183+
takes_tuple_any_one(Tuple_obj_one)
184+
takes_tuple_any_one(tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]"
185+
takes_tuple_any_one(Tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]"
186+
takes_tuple_any_one(tuple_any_implicit)
187+
takes_tuple_any_one(Tuple_any_implicit)
188+
takes_tuple_any_one(tuple_any)
189+
takes_tuple_any_one(Tuple_any)
190+
takes_tuple_any_one(tuple_any_one)
191+
takes_tuple_any_one(Tuple_any_one)
192+
takes_tuple_any_one(tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]"
193+
takes_tuple_any_one(Tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]"
119194

120195
class A: pass
121196
[builtins fixtures/tuple.pyi]
122197

198+
[case testSubtypingWithTupleTypeSubclass]
199+
from __future__ import annotations
200+
from typing import Any, Tuple
201+
202+
class A: ...
203+
204+
inst_tuple_aa: Tuple[A, A]
205+
206+
class tuple_aa_subclass(Tuple[A, A]): ...
207+
inst_tuple_aa_subclass: tuple_aa_subclass
208+
209+
class tuple_any_subclass(Tuple[Any, ...]): ...
210+
inst_tuple_any_subclass: tuple_any_subclass
211+
212+
class tuple_any_one_subclass(Tuple[Any]): ...
213+
inst_tuple_any_one_subclass: tuple_any_one_subclass
214+
215+
class tuple_any_two_subclass(Tuple[Any, Any]): ...
216+
inst_tuple_any_two_subclass: tuple_any_two_subclass
217+
218+
class tuple_obj_subclass(Tuple[object, ...]): ...
219+
inst_tuple_obj_subclass: tuple_obj_subclass
220+
221+
class tuple_obj_one_subclass(Tuple[object]): ...
222+
inst_tuple_obj_one_subclass: tuple_obj_one_subclass
223+
224+
class tuple_obj_two_subclass(Tuple[object, object]): ...
225+
inst_tuple_obj_two_subclass: tuple_obj_two_subclass
226+
227+
def takes_tuple_aa(t: Tuple[A, A]): ...
228+
229+
takes_tuple_aa(inst_tuple_aa)
230+
takes_tuple_aa(inst_tuple_aa_subclass)
231+
takes_tuple_aa(inst_tuple_any_subclass)
232+
takes_tuple_aa(inst_tuple_any_one_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_any_one_subclass"; expected "Tuple[A, A]"
233+
takes_tuple_aa(inst_tuple_any_two_subclass)
234+
takes_tuple_aa(inst_tuple_obj_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_subclass"; expected "Tuple[A, A]"
235+
takes_tuple_aa(inst_tuple_obj_one_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_one_subclass"; expected "Tuple[A, A]"
236+
takes_tuple_aa(inst_tuple_obj_two_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_two_subclass"; expected "Tuple[A, A]"
237+
238+
def takes_tuple_aa_subclass(t: tuple_aa_subclass): ...
239+
240+
takes_tuple_aa_subclass(inst_tuple_aa) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "Tuple[A, A]"; expected "tuple_aa_subclass"
241+
takes_tuple_aa_subclass(inst_tuple_aa_subclass)
242+
takes_tuple_aa_subclass(inst_tuple_any_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_subclass"; expected "tuple_aa_subclass"
243+
takes_tuple_aa_subclass(inst_tuple_any_one_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_one_subclass"; expected "tuple_aa_subclass"
244+
takes_tuple_aa_subclass(inst_tuple_any_two_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_any_two_subclass"; expected "tuple_aa_subclass"
245+
takes_tuple_aa_subclass(inst_tuple_obj_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_obj_subclass"; expected "tuple_aa_subclass"
246+
takes_tuple_aa_subclass(inst_tuple_obj_one_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_obj_one_subclass"; expected "tuple_aa_subclass"
247+
takes_tuple_aa_subclass(inst_tuple_obj_two_subclass) # E: Argument 1 to "takes_tuple_aa_subclass" has incompatible type "tuple_obj_two_subclass"; expected "tuple_aa_subclass"
248+
249+
[builtins fixtures/tuple.pyi]
250+
123251
[case testTupleInitializationWithNone]
124252
# flags: --no-strict-optional
125253
from typing import Tuple
@@ -1522,3 +1650,21 @@ class Bar(aaaaaaaaaa): # E: Name "aaaaaaaaaa" is not defined
15221650
class FooBarTuple(Tuple[Foo, Bar]):
15231651
...
15241652
[builtins fixtures/tuple.pyi]
1653+
1654+
1655+
[case testTupleOverloadZipAny]
1656+
from typing import Any, Iterable, Iterator, Tuple, TypeVar, overload
1657+
1658+
T = TypeVar("T")
1659+
1660+
@overload
1661+
def zip(__i: Iterable[T]) -> Iterator[Tuple[T]]: ...
1662+
@overload
1663+
def zip(*i: Iterable[Any]) -> Iterator[Tuple[Any, ...]]: ...
1664+
def zip(i): ...
1665+
1666+
def g(t: Tuple):
1667+
# Ideally, we'd infer that these are iterators of tuples
1668+
reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[Any]"
1669+
reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Any]"
1670+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)