Skip to content

Commit

Permalink
Let the multiple inheritance checks consider callable objects as poss…
Browse files Browse the repository at this point in the history
…ible subtypes of usual functions (Fixes python#14852).

The solution is inspired by the `visit_instance` method of `SubtypeVisitor`.  The `testMultipleInheritanceOverridingOfFunctionsWithCallableInstances` tests the new functionality for decorated and non-decorated functions and callable objects.
  • Loading branch information
tyralla committed Mar 8, 2023
1 parent 2523095 commit 0cb305f
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 1 deletion.
9 changes: 8 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2484,7 +2484,14 @@ class C(B, A[int]): ... # this is unsafe because...
first_type = get_proper_type(self.determine_type_of_member(first))
second_type = get_proper_type(self.determine_type_of_member(second))

if isinstance(first_type, FunctionLike) and isinstance(second_type, FunctionLike):
# start with the special case that Instance can be a subtype of FunctionLike
call = None
if isinstance(first_type, Instance):
call = find_member("__call__", first_type, first_type, is_operator=True)
if call and isinstance(second_type, FunctionLike):
second_sig = self.bind_and_map_method(second, second_type, ctx, base2)
ok = is_subtype(call, second_sig, ignore_pos_arg_names=True)
elif isinstance(first_type, FunctionLike) and isinstance(second_type, FunctionLike):
if first_type.is_type_obj() and second_type.is_type_obj():
# For class objects only check the subtype relationship of the classes,
# since we allow incompatible overrides of '__init__'/'__new__'
Expand Down
38 changes: 38 additions & 0 deletions test-data/unit/check-multiple-inheritance.test
Original file line number Diff line number Diff line change
Expand Up @@ -668,3 +668,41 @@ class D1(B[str], C1): ...
class D2(B[Union[int, str]], C2): ...
class D3(C2, B[str]): ...
class D4(B[str], C2): ... # E: Definition of "foo" in base class "A" is incompatible with definition in base class "C2"


[case testMultipleInheritanceOverridingOfFunctionsWithCallableInstances]
from typing import Any, Callable

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

class F:
def __call__(self, x: int) -> None: ...

def dec2(f: Callable[[Any, int], None]) -> F: ...

class B1:
def f(self, x: int) -> None: ...

class B2:
@dec1
def f(self, x: int) -> None: ...

class B3:
@dec2
def f(self, x: int) -> None: ...

class B4:
f = F()

class C12(B1, B2): ...
class C13(B1, B3): ... # E: Definition of "f" in base class "B1" is incompatible with definition in base class "B3"
class C14(B1, B4): ... # E: Definition of "f" in base class "B1" is incompatible with definition in base class "B4"
class C21(B2, B1): ...
class C23(B2, B3): ... # E: Definition of "f" in base class "B2" is incompatible with definition in base class "B3"
class C24(B2, B4): ... # E: Definition of "f" in base class "B2" is incompatible with definition in base class "B4"
class C31(B3, B1): ...
class C32(B3, B2): ...
class C34(B3, B4): ...
class C41(B4, B1): ...
class C42(B4, B2): ...
class C43(B4, B3): ...

0 comments on commit 0cb305f

Please sign in to comment.