Skip to content

Unexpected error when narrowing TypeVar #14045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pganssle opened this issue Nov 9, 2022 · 4 comments
Closed

Unexpected error when narrowing TypeVar #14045

pganssle opened this issue Nov 9, 2022 · 4 comments

Comments

@pganssle
Copy link
Member

pganssle commented Nov 9, 2022

Bug report

I ran into this recently and @glyph came up with a very elegant minimal working example:

from typing import TypeVar

T = TypeVar("T")

def f(s: T) -> T:
    if isinstance(s, str):
        return s # main.py:7: error: Incompatible return value type (got "str", expected "T")
    return s

Basically, if you have a function that takes a typevar and you narrow it using an isinstance check, mypy decides that the concrete type does not match the generic type from the signature, despite the fact that the concrete type is just the narrowed generic.

The best workaround I've found is to return cast(T, s). Glyph and I both tried playing around with overloads and couldn't find something to work around the issue other than just a cast.

@JelleZijlstra JelleZijlstra transferred this issue from python/cpython Nov 9, 2022
@glyph
Copy link

glyph commented Nov 9, 2022

I should note that there are some other cases which make this complicated:

from typing import TypeVar, reveal_type

T = TypeVar("T")

def f(a: T, b: T) -> T:
    return a

reveal_type(f("1", 2))

T is object in this call, due to that being the common supertype of the two arguments. So the TypeVar itself, T, is actually widened, to include str but not necessarily to exclude anything else. For example, if isinstance(a, str) is true, we can say nothing about b; but we can say that another str should be an acceptable return type.

Ooh. Except, maybe not:

from typing import TypeVar, reveal_type, NewType

T = TypeVar("T")

Special = NewType("Special", str)
TheSpecial = Special("Hello")

def f(a: T) -> T:
    if isinstance(a, str):
        return "Goodbye"
    return a

reveal_type(f(TheSpecial))

If "Hello" is the only Special string allowed, then the runtime check tells us nothing here.

Maybe this is not trivially possible.

@sterliakov
Copy link
Collaborator

Is it the same issue as #13956?

@glyph
Copy link

glyph commented Jan 10, 2023

Is it the same issue as #13956?

I believe so.

@hauntsaninja
Copy link
Collaborator

Closing as duplicate of #10817

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants