python / mypy

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

Incorrect type inference when using generics of "union" TypeVars #7913

Open wadetregaskis-linkedin opened 5 years ago

wadetregaskis-linkedin commented 5 years ago
from typing import MutableSet, Sequence, TypeVar

T = TypeVar('T', str, Sequence[str])

class CustomSet(MutableSet[T]):
    def add(self, value: T) -> None:
        pass

foo = CustomSet()
foo.add(('a', 'b'))

mypy 0.740 says: error: Argument 1 to "add" of "CustomSet" has incompatible type "Tuple[str, str]"; expected "str"

There's seemingly nothing I can do to get mypy to handle this pattern correctly. If I try explicitly specifying the type of foo, it just complains that the assignment to it is incompatible.

wadetregaskis-linkedin commented 5 years ago

And to be clear, this is irrespective of whether T includes Sequence[str] as in my real code from which this case is simplified, or an extremely literal Tuple[str, str] - it appears that no matter what, mypy stubbornly assumes T is always str. It's not related to the order in the TypeVar either; moving str to the end doesn't influence mypy.

wadetregaskis-linkedin commented 5 years ago

But changing str to int in the TypeVar does make mypy now stubbornly assume foo takes ints. For some reason mypy is picking the 'simplest' type in the TypeVar, to fixate on.

jonathanslenders commented 4 years ago

I think I have the same issue here:

import asyncio

async def f() -> None:
    future: asyncio.Future[None] = asyncio.Future()

    # error: Argument 1 to "gather" has incompatible type "Future[None]";
    #     expected "Union[Future[<nothing>], Generator[Any, None, <nothing>], Awaitable[<nothing>]]"
    await asyncio.gather(future)
JelleZijlstra commented 4 years ago

@jonathanslenders that sounds like #8051.

JukkaL commented 4 years ago

The inferred type for foo is incorrect (CustomSet[str]). Mypy should require a type annotation here.

You can work around the issue by adding an explicit type annotation:

...
foo: CustomSet[Sequence[str]] = CustomSet()
foo.add(('a', 'b'))
MarioIshac commented 4 years ago

I may have ran into same issue here:

from typing import Union, TypeVar, Sequence

T = TypeVar("T", bound=Union[int, str])

def a(t: Sequence[T]):
    return t

a([1, "a"])

results in:

Value of type variable "T" of "a" cannot be "object"

but

b: Sequence[Union[int, str]] = [1, "a"]
a(b)

works.