PEP 484 introduced a way to express “arbitrary-length homogeneous tuples”, but it didn’t specify the subtyping rules for these types.
Arbitrary-length homogeneous tuples can be expressed using one type and ellipsis, for example
Tuple[int, ...]
.
There are two logical ways to treat such tuples from a type compatibility standpoint:
- As a gradual type. With this interpretation,
tuple[T, ...]
is compatible with a tuple of any length that contains homogeneous elements of typeT
, and the reverse is true as well. With this interpretation,tuple[T]
is compatible withtuple[T, ...]
and the converse is true as well. - As a union. With this interpretation,
tuple[T, ...]
is shorthand fortuple[()] | tuple[T] | tuple[T, T] | ...
. With this interpretation,tuple[T]
is compatible with (and a subtype of)tuple[T, ...]
, but the converse is not true.
Mypy implements interpretation 2. Pyright previously used interpretation 1, but about a year ago I changed pyright to use interpretation 2 for compatibility with mypy (which was the reference implementation for PEP 484 and therefore presumably the authority on the topic).
Pyre and pytype appear to use interpretation 1.
Code sample in pyright playground
Code sample in mypy playground
def func1(p1: tuple[str, ...]):
t1: tuple[()] = p1 # Type error under interpretation 2
t2: tuple[str] = p1 # Type error under interpretation 2
t3: tuple[str, ...] = p1 # OK
p1 = () # OK
p1 = ("",) # OK
p1 = ("", "") # OK
My personal preference is interpretation 2, but I’d like to hear what others think.
Regardless, I’d like to achieve consensus and formalize the rule in the typing spec.