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.
Is there any workaround to this now? I'm trying to get my Flask return types correct.
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).
(In view of coming implementation of recursive types this will be important so raising priority to high.)
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.
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.