Skip to content

Fix tuple[Any, ...] subtyping #16108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1359,7 +1359,7 @@ def set_strict_flags() -> None:
parser.error("Can only find occurrences of class members.")
if len(_find_occurrences) != 2:
parser.error("Can only find occurrences of non-nested class members.")
state.find_occurrences = _find_occurrences # type: ignore[assignment]
state.find_occurrences = _find_occurrences

# Set reports.
for flag, val in vars(special_opts).items():
Expand Down
6 changes: 4 additions & 2 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,8 +463,10 @@ def visit_instance(self, left: Instance) -> bool:
assert unpacked.type.fullname == "builtins.tuple"
if isinstance(get_proper_type(unpacked.args[0]), AnyType):
return not self.proper_subtype
# TODO: we need a special case similar to above to consider (something that maps to)
# tuple[Any, ...] a subtype of Tuple[<whatever>].
if mapped.type.fullname == "builtins.tuple" and isinstance(
get_proper_type(mapped.args[0]), AnyType
):
return not self.proper_subtype
return False
if isinstance(right, TypeVarTupleType):
# tuple[Any, ...] is like Any in the world of tuples (see special case above).
Expand Down
164 changes: 155 additions & 9 deletions test-data/unit/check-tuples.test
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,147 @@ class A: pass
class B(A): pass
[builtins fixtures/tuple.pyi]

[case testSubtypingWithNamedTupleType]
from typing import Tuple
t1: Tuple[A, A]
t2: tuple

if int():
t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[Any, ...]", variable has type "Tuple[A, A]")
if int():
t2 = t1
[case testSubtypingWithTupleType]
from __future__ import annotations
from typing import Any, Tuple

tuple_aa: tuple[A, A]
Tuple_aa: Tuple[A, A]

tuple_obj: tuple[object, ...]
Tuple_obj: Tuple[object, ...]

tuple_obj_one: tuple[object]
Tuple_obj_one: Tuple[object]

tuple_obj_two: tuple[object, object]
Tuple_obj_two: Tuple[object, object]

tuple_any_implicit: tuple
Tuple_any_implicit: Tuple

tuple_any: tuple[Any, ...]
Tuple_any: Tuple[Any, ...]

tuple_any_one: tuple[Any]
Tuple_any_one: Tuple[Any]

tuple_any_two: tuple[Any, Any]
Tuple_any_two: Tuple[Any, Any]

def takes_tuple_aa(t: tuple[A, A]): ...

takes_tuple_aa(tuple_aa)
takes_tuple_aa(Tuple_aa)
takes_tuple_aa(tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]"
takes_tuple_aa(Tuple_obj) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, ...]"; expected "Tuple[A, A]"
takes_tuple_aa(tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]"
takes_tuple_aa(Tuple_obj_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object]"; expected "Tuple[A, A]"
takes_tuple_aa(tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]"
takes_tuple_aa(Tuple_obj_two) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[object, object]"; expected "Tuple[A, A]"
takes_tuple_aa(tuple_any_implicit)
takes_tuple_aa(Tuple_any_implicit)
takes_tuple_aa(tuple_any)
takes_tuple_aa(Tuple_any)
takes_tuple_aa(tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]"
takes_tuple_aa(Tuple_any_one) # E: Argument 1 to "takes_tuple_aa" has incompatible type "Tuple[Any]"; expected "Tuple[A, A]"
takes_tuple_aa(tuple_any_two)
takes_tuple_aa(Tuple_any_two)

def takes_tuple_any_implicit(t: tuple): ...

takes_tuple_any_implicit(tuple_aa)
takes_tuple_any_implicit(Tuple_aa)
takes_tuple_any_implicit(tuple_obj)
takes_tuple_any_implicit(Tuple_obj)
takes_tuple_any_implicit(tuple_obj_one)
takes_tuple_any_implicit(Tuple_obj_one)
takes_tuple_any_implicit(tuple_obj_two)
takes_tuple_any_implicit(Tuple_obj_two)
takes_tuple_any_implicit(tuple_any_implicit)
takes_tuple_any_implicit(Tuple_any_implicit)
takes_tuple_any_implicit(tuple_any)
takes_tuple_any_implicit(Tuple_any)
takes_tuple_any_implicit(tuple_any_one)
takes_tuple_any_implicit(Tuple_any_one)
takes_tuple_any_implicit(tuple_any_two)
takes_tuple_any_implicit(Tuple_any_two)

def takes_tuple_any_one(t: tuple[Any]): ...

takes_tuple_any_one(tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]"
takes_tuple_any_one(Tuple_aa) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[A, A]"; expected "Tuple[Any]"
takes_tuple_any_one(tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]"
takes_tuple_any_one(Tuple_obj) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, ...]"; expected "Tuple[Any]"
takes_tuple_any_one(tuple_obj_one)
takes_tuple_any_one(Tuple_obj_one)
takes_tuple_any_one(tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]"
takes_tuple_any_one(Tuple_obj_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[object, object]"; expected "Tuple[Any]"
takes_tuple_any_one(tuple_any_implicit)
takes_tuple_any_one(Tuple_any_implicit)
takes_tuple_any_one(tuple_any)
takes_tuple_any_one(Tuple_any)
takes_tuple_any_one(tuple_any_one)
takes_tuple_any_one(Tuple_any_one)
takes_tuple_any_one(tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]"
takes_tuple_any_one(Tuple_any_two) # E: Argument 1 to "takes_tuple_any_one" has incompatible type "Tuple[Any, Any]"; expected "Tuple[Any]"

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

[case testSubtypingWithTupleTypeSubclass]
from __future__ import annotations
from typing import Any, Tuple

class A: ...

inst_tuple_aa: Tuple[A, A]

class tuple_aa_subclass(Tuple[A, A]): ...
inst_tuple_aa_subclass: tuple_aa_subclass

class tuple_any_subclass(Tuple[Any, ...]): ...
inst_tuple_any_subclass: tuple_any_subclass

class tuple_any_one_subclass(Tuple[Any]): ...
inst_tuple_any_one_subclass: tuple_any_one_subclass

class tuple_any_two_subclass(Tuple[Any, Any]): ...
inst_tuple_any_two_subclass: tuple_any_two_subclass

class tuple_obj_subclass(Tuple[object, ...]): ...
inst_tuple_obj_subclass: tuple_obj_subclass

class tuple_obj_one_subclass(Tuple[object]): ...
inst_tuple_obj_one_subclass: tuple_obj_one_subclass

class tuple_obj_two_subclass(Tuple[object, object]): ...
inst_tuple_obj_two_subclass: tuple_obj_two_subclass

def takes_tuple_aa(t: Tuple[A, A]): ...

takes_tuple_aa(inst_tuple_aa)
takes_tuple_aa(inst_tuple_aa_subclass)
takes_tuple_aa(inst_tuple_any_subclass)
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]"
takes_tuple_aa(inst_tuple_any_two_subclass)
takes_tuple_aa(inst_tuple_obj_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_obj_subclass"; expected "Tuple[A, A]"
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]"
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]"

def takes_tuple_aa_subclass(t: tuple_aa_subclass): ...

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"
takes_tuple_aa_subclass(inst_tuple_aa_subclass)
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"
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"
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"
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"
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"
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"

[builtins fixtures/tuple.pyi]

[case testTupleInitializationWithNone]
# flags: --no-strict-optional
from typing import Tuple
Expand Down Expand Up @@ -1522,3 +1650,21 @@ class Bar(aaaaaaaaaa): # E: Name "aaaaaaaaaa" is not defined
class FooBarTuple(Tuple[Foo, Bar]):
...
[builtins fixtures/tuple.pyi]


[case testTupleOverloadZipAny]
from typing import Any, Iterable, Iterator, Tuple, TypeVar, overload

T = TypeVar("T")

@overload
def zip(__i: Iterable[T]) -> Iterator[Tuple[T]]: ...
@overload
def zip(*i: Iterable[Any]) -> Iterator[Tuple[Any, ...]]: ...
def zip(i): ...

def g(t: Tuple):
# Ideally, we'd infer that these are iterators of tuples
reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[Any]"
reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Any]"
[builtins fixtures/tuple.pyi]