Skip to content
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

filtering with isinstance doesn't narrow down types #6847

Closed
YtvwlD opened this issue May 16, 2019 · 4 comments
Closed

filtering with isinstance doesn't narrow down types #6847

YtvwlD opened this issue May 16, 2019 · 4 comments
Labels
feature priority-2-low topic-type-narrowing Conditional type narrowing / binder

Comments

@YtvwlD
Copy link

YtvwlD commented May 16, 2019

I had about this code (this is a small example):

from typing import List, Union

class TypeA: pass
class TypeB: pass

Types = Union[TypeA, TypeB]
t: List[Types] = [TypeA(), TypeB()]
a: List[TypeA] = list(filter(lambda x: isinstance(x, TypeA), t))

And I got the following message (both with my distribution's mypy 0.560 and with the current master):

error: Argument 2 to "filter" has incompatible type "List[Union[TypeA, TypeB]]"; expected "Iterable[TypeA]"

When I remove the type after a, it treats a as List[Types].

I get that this might be difficult, but it would be nice if it worked.

This could be related to #476, I'm not sure.

@WillAyd
Copy link

WillAyd commented May 18, 2019

I think this would be another example if useful:

from pathlib import Path
from typing import Union

def maybe_lowercase(obj: Union[str, Path]) -> Union[str, Path]:
    if isinstance(obj, str):
        return obj.lower()

    return obj

def foo(obj: Union[str, Path]) -> str:
    if isinstance(obj, Path):
        return str(obj)

    return maybe_lowercase(obj)

In foo the last return cannot be a Path given the preceding isinstance check, but it doesn't seem to narrow the types and complains because maybe_lowercase is typed to return a Path in it's union:

error: Incompatible return value type (got "Union[str, Path]", expected "str")

@JelleZijlstra
Copy link
Member

@WillAyd your example should use a TypeVar with value restriction to str and Path for maybe_lowercase not a union. The signature you use does not mean that maybe_lowercase will return a str when given a str.

@ilevkivskyi
Copy link
Member

I get that this might be difficult, but it would be nice if it worked.

Yes, this is too tricky. There is a plan to support custom type guards, you can probably use that instead. I am leaving this open as a remainder about the use case.

@AlexWaygood
Copy link
Member

TypeGuard has now been implemented in mypy, and the typeshed stub for filter has been updated to include an overload for TypeGuard functions. That means if I modify the original repro to this:

from typing import List, Union, TypeGuard

class TypeA: pass
class TypeB: pass

def is_TypeA(x: object) -> TypeGuard[TypeA]:
    return isinstance(x, TypeA)

Types = Union[TypeA, TypeB]
t: List[Types] = [TypeA(), TypeB()]
a: List[TypeA] = list(filter(is_TypeA, t))

and then (inside my local clone of typeshed) run mypy test_script.py --custom-typeshed-dir ., I get no errors. Hooray!

mypy's vendored copy of typeshed has yet to include the update to the stub for filter, but this should hopefully be fixed in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature priority-2-low topic-type-narrowing Conditional type narrowing / binder
Projects
None yet
Development

No branches or pull requests

5 participants