From 2c2d126cc742f2467045d36780c33bb8fb77a614 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:27:54 -0700 Subject: [PATCH] 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. --- mypy/main.py | 2 +- mypy/subtypes.py | 6 +- test-data/unit/check-tuples.test | 164 +++++++++++++++++++++++++++++-- 3 files changed, 160 insertions(+), 12 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index a4357dca7890..3eb8a76a6de3 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -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(): diff --git a/mypy/subtypes.py b/mypy/subtypes.py index e8339a8c4d69..9ed2e4af4051 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -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[]. + 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). diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 0e7c81edc498..391fa20db738 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -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 @@ -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]