Skip to content

Commit

Permalink
Fix crash with overload and callable object decorators (python#11630)
Browse files Browse the repository at this point in the history
Fixes python#8356, as identified by @pranavrajpal
Fixes python#9112
Fixes python#9967

Note that we still don't fully support the singledispatch pattern in python#8356,
since we get 'overloaded function has no attribute "register"', but that's
much easier to work around than a crash.

Co-authored-by: hauntsaninja <>
  • Loading branch information
hauntsaninja authored and tushar-deepsource committed Jan 20, 2022
1 parent e87f580 commit 713df5e
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 2 deletions.
12 changes: 10 additions & 2 deletions mypy/checker.py
Expand Up @@ -508,8 +508,16 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
# decorator or if the implementation is untyped -- we gave up on the types.
inner_type = get_proper_type(inner_type)
if inner_type is not None and not isinstance(inner_type, AnyType):
assert isinstance(inner_type, CallableType)
impl_type = inner_type
if isinstance(inner_type, CallableType):
impl_type = inner_type
elif isinstance(inner_type, Instance):
inner_call = get_proper_type(
find_member('__call__', inner_type, inner_type, is_operator=True)
)
if isinstance(inner_call, CallableType):
impl_type = inner_call
if impl_type is None:
self.msg.not_callable(inner_type, defn.impl)

is_descriptor_get = defn.info and defn.name == "__get__"
for i, item in enumerate(defn.items):
Expand Down
68 changes: 68 additions & 0 deletions test-data/unit/check-overloading.test
Expand Up @@ -5339,3 +5339,71 @@ def register(cls: Any) -> Any: return None
x = register(Foo)
reveal_type(x) # N: Revealed type is "builtins.int"
[builtins fixtures/dict.pyi]


[case testOverloadWithObjectDecorator]
from typing import Any, Callable, Union, overload

class A:
def __call__(self, *arg, **kwargs) -> None: ...

def dec_a(f: Callable[..., Any]) -> A:
return A()

@overload
def f_a(arg: int) -> None: ...
@overload
def f_a(arg: str) -> None: ...
@dec_a
def f_a(arg): ...

class B:
def __call__(self, arg: Union[int, str]) -> None: ...

def dec_b(f: Callable[..., Any]) -> B:
return B()

@overload
def f_b(arg: int) -> None: ...
@overload
def f_b(arg: str) -> None: ...
@dec_b
def f_b(arg): ...

class C:
def __call__(self, arg: int) -> None: ...

def dec_c(f: Callable[..., Any]) -> C:
return C()

@overload
def f_c(arg: int) -> None: ...
@overload
def f_c(arg: str) -> None: ...
@dec_c # E: Overloaded function implementation does not accept all possible arguments of signature 2
def f_c(arg): ...
[builtins fixtures/dict.pyi]

[case testOverloadWithErrorDecorator]
from typing import Any, Callable, TypeVar, overload

def dec_d(f: Callable[..., Any]) -> int: ...

@overload
def f_d(arg: int) -> None: ...
@overload
def f_d(arg: str) -> None: ...
@dec_d # E: "int" not callable
def f_d(arg): ...

Bad = TypeVar('Good') # type: ignore

def dec_e(f: Bad) -> Bad: ... # type: ignore

@overload
def f_e(arg: int) -> None: ...
@overload
def f_e(arg: str) -> None: ...
@dec_e # E: Bad? not callable
def f_e(arg): ...
[builtins fixtures/dict.pyi]

0 comments on commit 713df5e

Please sign in to comment.