Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
8 changes: 6 additions & 2 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
mapped.type.has_base("builtins.tuple")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you need the last commit? This condition is always true because of the surrounding if (each tuple fallback has builtins.tuple in MRO), and also now this check looks wrong because it may trigger in unrelated cases involving something like class M(Generic[T], tuple[int, ...]): ....

What you had before was almost right, except you didn't need the check len(mapped.args) == 1, it should be always true if mapped.type.fullname == "builtins.tuple".

Copy link
Copy Markdown
Collaborator Author

@hauntsaninja hauntsaninja Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I misinterpreted your comment here #16108 (comment)

Ugh, I tried to test the class M(Generic[T], tuple[int, ...]): ... case but I did it backwards I can't actually find a test, even with various NamedTuples

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you will not be able to come up with a test case, don't try to hard. It may be not easy to trigger such edge case (because of how user defined tuple types are represented). You already have enough test cases :-)

(I would still remove the unneeded len check)

and len(mapped.args) == 1
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]