-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Changes from 3 commits
2044720
6a4df2b
c2250f8
e64a6cd
17ad1ca
d5812b6
03fda8b
9840c37
2c4f791
e99d2f0
5c03731
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -463,8 +463,12 @@ 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 ( | ||
| left.type.fullname == "builtins.tuple" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should use |
||
| and len(left.args) == 1 | ||
| and isinstance(get_proper_type(left.args[0]), AnyType) | ||
| ): | ||
| return True | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should return |
||
| return False | ||
| if isinstance(right, TypeVarTupleType): | ||
| # tuple[Any, ...] is like Any in the world of tuples (see special case above). | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,19 +107,148 @@ 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) | ||
| # This next one should maybe not be an error | ||
| takes_tuple_aa(inst_tuple_any_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_any_subclass"; expected "Tuple[A, A]" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be allowed (see above, if |
||
| 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 +1651,20 @@ 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): | ||
| reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[builtins.tuple[Any, ...]]" | ||
| reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Tuple[Any]]" | ||
| [builtins fixtures/tuple.pyi] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks suspicious, the check below is for proper subtype, that should not now anything about
Any.