python mypy error with immutable list as tuple

Question:

I encounter a weird error while defining an immutable list type:

import sys
from re import split
from typing import Tuple, List, NewType

tokens = split("(d+.d+)", sys.argv[1])

# this works perfectly
# Tokens = NewType("Tokens", List[str])
# print(Tokens(tokens))

# this doesn't work ...
Tokens = NewType("Tokens", Tuple[str])
print(Tokens(tuple(tokens)))

Here is the error that I get:

> mypy main.py
main.py:13: error: Argument 1 to "Tokens" has incompatible type "Tuple[Union[str, Any], ...]"; expected "Tuple[str]"
Found 1 error in 1 file (checked 1 source file)
Asked By: OrenIshShalom

||

Answers:

Why the error occurs

When you using tuple() to transform a list to tuple, Python or at least Mypy doesn’t know the exact type of elements in the tuple. Thus the error occurs.
Using different approaches to transform the list to tuple leads to different Mypy error:

token_x = tuple(tokens)
token_y = tuple(i for i in tokens)
token_z = (*tokens, )
main.py:15: error: Argument 1 to "Tokens" has incompatible type "Tuple[Union[str, Any], ...]"; expected "Tuple[str]"
main.py:16: error: Argument 1 to "Tokens" has incompatible type "Tuple[Union[str, Any], ...]"; expected "Tuple[str]"
main.py:17: error: Argument 1 to "Tokens" has incompatible type "Tuple[Any, ...]"; expected "Tuple[str]"

Solution

import sys
from re import split
from typing import Tuple, List, NewType

tokens: List[str] = split("(d+.d+)", sys.argv[1]) # explicit annotation on here is great but not necessary
token_x = tuple(tokens)
token_y = tuple(i for i in tokens)
token_z = (*tokens,)

# this works perfectly
# Tokens = NewType("Tokens", List[str])
# print(Tokens(tokens))

# this doesn't work ...
Tokens = NewType("Tokens", Tuple[str, ...]) # explicitly annotate with Tuple[str, ...]
Tokens = NewType("Tokens", Sequence[str]) # or using Sequence[str], which is recommended by Mypy offical document
print(Tokens(token_x))
print(Tokens(token_y))
print(Tokens(token_z))

Appendix

Related question: How to annotate function that takes a tuple of variable length? (variadic tuple type annotation)

Meaning of Tuple[str, ...]: str, ... stands of all the elements in the tuple are str

Mypy offical Doc: https://mypy.readthedocs.io/en/stable/kinds_of_types.html#tuple-types

Answered By: Will Zhao