mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Type not narrowed on `isinstance(obj, Callable)` check

Open erictraut opened this issue 4 years ago • 5 comments

I received this bug report from a pyright user: https://github.com/microsoft/pyright/issues/2274. Mypy exhibits the same behavior, so I'm wondering whether it's a bug or the intended behavior.

In this code sample, mypy (and pyright) do not currently narrow the type of obj based on the isinstance(obj, Callable) conditional check.

from typing import Callable, Union

def some_func(obj: Union[Callable, int]):
    if isinstance(obj, Callable):
        reveal_type(obj) # Type of "obj" is "(*args: Unknown, **kwargs: Unknown) -> Unknown | int"
    else:
        reveal_type(obj) # Type of "obj" is "(*args: Unknown, **kwargs: Unknown) -> Unknown | int"

I'm wondering if there's a reason why mypy does not perform type narrowing in this case. Is there a type safety reason? Is callable() the preferred way to check for callables?

I've searched the issue database, and it doesn't appear to be an issue that anyone has previously reported.

erictraut avatar Sep 08 '21 01:09 erictraut

PEP 484 says:

Because typing.Callable does double-duty as a replacement for collections.abc.Callable, isinstance(x, typing.Callable) is implemented by deferring to isinstance(x, collections.abc.Callable). However, isinstance(x, typing.Callable[...]) is not supported.

I believe mypy misses this because we generally reject typing module items from isinstance checks, and this is an exception.

emmatyping avatar Sep 08 '21 01:09 emmatyping

@ethanhs

Thanks so much. This was driving me mad.

I was using Callable[...] | None and I just could not get type narrowing to pass isinstance(handler, Callable) So changed this to isinstance(handler, collections.abc.Callable) and it now works fine.

Is there any reason that typing.Callable couldn't be made to work with isinstance etc in the long term? It would make more sense.

andrewcrook avatar Jul 31 '22 16:07 andrewcrook

I received this bug report from a pyright user: microsoft/pyright#2274. Mypy exhibits the same behavior, so I'm wondering whether it's a bug or the intended behavior.

@erictraut FYI I am seeing the same behaviour in pylance v2022.7.40 / pyright 1.1.260 still have to use collections.abc.Callable

andrewcrook avatar Jul 31 '22 21:07 andrewcrook

@andrewcrook, I'm not able to repro the issue in pyright or pylance. If you think there is a bug, please file a bug report in the pyright issue tracker.

erictraut avatar Jul 31 '22 21:07 erictraut

@erictraut Of course I will , I was just updating you because of the link you also mentioned.

andrewcrook avatar Jul 31 '22 21:07 andrewcrook

The output of Eric's sample is now

main.py:4: error: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "_ClassInfo"  [arg-type]
main.py:5: note: Revealed type is "Union[def (*Any, **Any) -> Any, builtins.int]"
main.py:7: note: Revealed type is "Union[def (*Any, **Any) -> Any, builtins.int]"
Found 1 error in 1 file (checked 1 source file)

Which is probably worse. We need to special-case Callable in some way.

JelleZijlstra avatar Mar 28 '24 14:03 JelleZijlstra