microsoft / pyright

Static Type Checker for Python
Other
13.48k stars 1.48k forks source link

Difference between `callable(param)` and `isinstance(param, typing.Callable)` #9444

Closed timrid closed 1 week ago

timrid commented 2 weeks ago

Describe the bug I would expect that the following two examples are equal, but callable(param) creates an error since pyright v1.1.385.

Code or Screenshots

from typing import Any, Callable, TypeVar

T = TypeVar("T")

def evaluate2(param: T | Callable[[Any], T], context: Any) -> T:
    if callable(param):
        return param(context)  # <--- ERROR since pyright v1.1.385
    return param

def evaluate3(param: T | Callable[[Any], T], context: Any) -> T:
    if isinstance(param, Callable):
        return param(context)
    return param

Image

VS Code extension or command-line VSCode pylance v2024.11.1 or pyright since v1.1.385 (in v1.1.384 it was not an error)

erictraut commented 1 week ago

Pyright is working as intended here. The callable(x) type guard behavior is dictated by the typeshed definition of the callable function, which is defined as follows:

def callable(obj: object, /) -> TypeIs[Callable[..., object]]: ...

The isinstance(x, T) type guard behavior is provided by custom logic within pyright.