python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.46k stars 2.83k forks source link

Union subtyping doesn't work with covariant type constructors Tuple and Callable #7187

Open ilevkivskyi opened 5 years ago

ilevkivskyi commented 5 years ago

Both calls in this code fail, but I think they should be allowed:

from typing import Callable, Union, Tuple

def f(x: Union[Callable[..., int], Callable[..., str]]) -> None: ...
x: Callable[..., Union[int, str]]
f(x)

def g(y: Union[Tuple[int], Tuple[str]]) -> None: ...
y: Tuple[Union[int, str]]
g(y)

Moreover, I would say types in each pair are actually equivalent.

mortoray commented 5 years ago

Is there any workaround to this now? I'm trying to get my Flask return types correct.

JukkaL commented 5 years ago

There are a few options. You can use an Any return type. You can use # type: ignore. Sometimes you can use an overloaded function:

@overload
def g(x: int) -> int: ...
@overload
def g(x: str) -> str: ...
def g(x) -> Union[int, str]:
    return ...

f(g)  # OK

Perhaps the best option would to change the stubs for flask to use a union return type (the change was made in https://github.com/python/typeshed/pull/3003/files).

ilevkivskyi commented 5 years ago

(In view of coming implementation of recursive types this will be important so raising priority to high.)

jingw commented 5 years ago

Are those types actually equivalent? Union[Callable[..., int], Callable[..., str]] sounds like a function that always returns ints or always returns strings, whereas Callable[..., Union[int, str]] is a function that can return a different thing each time. This argument does not apply to the tuple example, though it would if it was a list.

ilevkivskyi commented 5 years ago

For mypy if x has type Union[int, str] then [x, x, x] has type List[Union[int, str]], not Union[List[int], List[str]], and this is not easy to fix. So the difference you mention is not essential ATM.

Anyway, we need to fix subtyping for unions in Callable and Tuple, it is currently broken.

tjprescott commented 9 months ago

Is there any movement on this? I'm trying to use mypy with Flask and the only way to make it happy is to make the return type Response which means it could be anything. Otherwise I get the Value of type variable "T_route" of function cannot be "Callable[[], Foo]"