Skip to content

Commit a7a995a

Browse files
tyrallaJukkaL
authored andcommitted
Multiple inheritance considers callable objects as subtypes of functions (#14855)
Let the multiple inheritance checks consider callable objects as possible subtypes of usual functions (Fixes #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.
1 parent 7f2a5b5 commit a7a995a

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

Diff for: mypy/checker.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -2485,7 +2485,14 @@ class C(B, A[int]): ... # this is unsafe because...
24852485
first_type = get_proper_type(self.determine_type_of_member(first))
24862486
second_type = get_proper_type(self.determine_type_of_member(second))
24872487

2488-
if isinstance(first_type, FunctionLike) and isinstance(second_type, FunctionLike):
2488+
# start with the special case that Instance can be a subtype of FunctionLike
2489+
call = None
2490+
if isinstance(first_type, Instance):
2491+
call = find_member("__call__", first_type, first_type, is_operator=True)
2492+
if call and isinstance(second_type, FunctionLike):
2493+
second_sig = self.bind_and_map_method(second, second_type, ctx, base2)
2494+
ok = is_subtype(call, second_sig, ignore_pos_arg_names=True)
2495+
elif isinstance(first_type, FunctionLike) and isinstance(second_type, FunctionLike):
24892496
if first_type.is_type_obj() and second_type.is_type_obj():
24902497
# For class objects only check the subtype relationship of the classes,
24912498
# since we allow incompatible overrides of '__init__'/'__new__'

Diff for: test-data/unit/check-multiple-inheritance.test

+38
Original file line numberDiff line numberDiff line change
@@ -668,3 +668,41 @@ class D1(B[str], C1): ...
668668
class D2(B[Union[int, str]], C2): ...
669669
class D3(C2, B[str]): ...
670670
class D4(B[str], C2): ... # E: Definition of "foo" in base class "A" is incompatible with definition in base class "C2"
671+
672+
673+
[case testMultipleInheritanceOverridingOfFunctionsWithCallableInstances]
674+
from typing import Any, Callable
675+
676+
def dec1(f: Callable[[Any, int], None]) -> Callable[[Any, int], None]: ...
677+
678+
class F:
679+
def __call__(self, x: int) -> None: ...
680+
681+
def dec2(f: Callable[[Any, int], None]) -> F: ...
682+
683+
class B1:
684+
def f(self, x: int) -> None: ...
685+
686+
class B2:
687+
@dec1
688+
def f(self, x: int) -> None: ...
689+
690+
class B3:
691+
@dec2
692+
def f(self, x: int) -> None: ...
693+
694+
class B4:
695+
f = F()
696+
697+
class C12(B1, B2): ...
698+
class C13(B1, B3): ... # E: Definition of "f" in base class "B1" is incompatible with definition in base class "B3"
699+
class C14(B1, B4): ... # E: Definition of "f" in base class "B1" is incompatible with definition in base class "B4"
700+
class C21(B2, B1): ...
701+
class C23(B2, B3): ... # E: Definition of "f" in base class "B2" is incompatible with definition in base class "B3"
702+
class C24(B2, B4): ... # E: Definition of "f" in base class "B2" is incompatible with definition in base class "B4"
703+
class C31(B3, B1): ...
704+
class C32(B3, B2): ...
705+
class C34(B3, B4): ...
706+
class C41(B4, B1): ...
707+
class C42(B4, B2): ...
708+
class C43(B4, B3): ...

0 commit comments

Comments
 (0)