Open
Description
From the typing spec:
The type
tuple[Any, ...]
is special in that it is consistent with all tuple types, and assignable to a tuple of any length. This is useful for gradual typing.
And mypy does precisely this:
type Ge0 = tuple[Any, ...]
def ge0_to_0(shape: Ge0) -> tuple[()]:
return shape # ✅ true negative
def ge0_to_1(shape: Ge0) -> tuple[int]:
return shape # ✅ true negative
def ge0_to_2(shape: Ge0) -> tuple[int, int]:
return shape # ✅ true negative
Ok, so far so good.
The typing spec also says the following:
If an unpacked
*tuple[Any, ...]
is embedded within another tuple, that portion of the tuple is consistent with any tuple of any length.
So tuple[int, *tuple[Any, ...]]
is assignable to any tuple with int
as first element. But mypy behaves differently:
type Ge1 = tuple[int, *tuple[Any, ...]]
def ge1_to_0(shape: Ge1) -> tuple[()]:
return shape # ✅ true positive
def ge1_to_1(shape: Ge1) -> tuple[int]:
return shape # ❌ false positive
def ge1_to_2(shape: Ge1) -> tuple[int, int]:
return shape # ❌ false positive
full output:
main.py:17: error: Incompatible return value type (got "tuple[int, *tuple[Any, ...]]", expected "tuple[()]") [return-value]
main.py:19: error: Incompatible return value type (got "tuple[int, *tuple[Any, ...]]", expected "tuple[int]") [return-value]
main.py:21: error: Incompatible return value type (got "tuple[int, *tuple[Any, ...]]", expected "tuple[int, int]") [return-value]
Found 3 errors in 1 file (checked 1 source file)
So it's only the last two errors that are false positives.
Some observations:
- This is the case with (at least) mypy 1.15.0 and on the current
master
branch. - It does not matter if you instead use
tuple[Any, *tuple[Any, ...]]
and/or useAny
in the return types. - Pyright behaves correctly here (playground)