3

Short version

tuple[int, ...] is a valid type in python 3.9+ but mypy rejects a type alias for that. E.g. consider:

Thing = tuple[int, ...]

This results in "error: Unexpected '...'" (though python itself will load the module just fine).

Is this a bug in mypy?

What should/can I do? I thought maybe I could just use NewType instead but that fails in the same way.

Long version

Python 3.9 incorporates PEP 585, meaning we can use built-in types such as list, set and tuple as generics; as such the versions in typing are now deprecated (e.g. see docs for typing.Tuple).

mypy 0.800 introduced support for this, so now I can write the following and mypy will type-check it correctly:

def foo(a: tuple[int, str]):
    ...

Now, tuple (and typing.Tuple) of course have a special form for homogenously-typed tuples of unknown length: tuple[int, ...]; this works fine too:

def foo(a: tuple[int, ...]):
    ...

Handily, we can create aliases for types, so this works too:

from typing import Tuple

Thing = Tuple[int, ...]

def foo(a: Thing):
    ...

Unfortunately, the following (which puts together tuple, the ... form, and a type alias) does not work:

Thing = tuple[int, ...]

def foo(a: Thing):
    ...

Rather, this is rejected by mypy (v0.812) with the message "error: Unexpected '...'"

Python itself will load and run such a module just fine, however.

I haven't (yet) been able to find anything indicating that this shouldn't be possible. So:

  • Am I missing something? Is such a type alias in fact forbidden?

  • If not, is this a bug in mypy?

  • Are there any better ways for working around this than just using NewType instead of an alias?

    I thought using NewType would work around this (with some extra work) but it seems to fail in exactly the same way. So right now there seems to be no way to write these types and keep mypy happy.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
gimboland
  • 1,926
  • 2
  • 19
  • 28
  • Am I mistaken in thinking you can just use `tuple[int]` which will be seen as the equivalent? – Marcel Wilson Apr 30 '21 at 19:25
  • 2
    @MarcelWilson That's a singlet tuple, which is different than `tuple[int, ...]`, which is a tuple of ints of non-fixed size. – Carcigenicate Apr 30 '21 at 19:28
  • 2
    This strikes me as a bug in mypy. – Carcigenicate Apr 30 '21 at 19:28
  • @Carcigenicate Yes, I've just discovered it's mypy issue 9980: https://github.com/python/mypy/issues/9980 — I thought I'd done my research before posting the question but then I dug a bit deeper and there it is... – gimboland Apr 30 '21 at 19:35
  • Do you really need to restrict the argument to `tuple` specifically, rather than using `Sequence[int]`? – chepner Apr 30 '21 at 19:48
  • (There are also `Container[int]`, `Iterable[int]`, `Collection[int]`, etc, depending on what your function need to do, exactly, with the argument.) – chepner Apr 30 '21 at 19:53
  • @chepner Thanks for those ideas... They're good points — but I've chosen to use `tuple` for its immutability, which is a guarantee I won't get with those other types (and AFAIK there aren't any that ensure it, as Python ABCs can't specify "doesn't implement X", right?). I'm afraid this is my time writing Haskell showing up in my Python... :-) Maybe I should relax a bit; I'll think about it. – gimboland May 04 '21 at 09:24
  • The abstract classes don't prevent you from using tuples; they just allow other (theoretical) immutable types as well. It's not a *tuple* you want, per se, but an immutable object that can be indexed. – chepner May 04 '21 at 12:25
  • (Though to be fair, the hierarchy doesn't strongly differentiate between mutable and immutable types. The mutable variants reject immutable types, but I don't think there's a way to specify a type that *must* be immutable.) – chepner May 04 '21 at 13:13

1 Answers1

2

Aha, looks like it is indeed an open mypy bug.

So now the only question (until that's fixed/released) is how to work around it? Probably I have to bite the NewType bullet. :-/


Edit: hilariously, Thing = NewType('Thing', tuple[int, ...]) fails in exactly the same way.

gimboland
  • 1,926
  • 2
  • 19
  • 28