@@ -678,13 +678,16 @@ def visit_callable_type(self, left: CallableType) -> bool:
678678 elif isinstance (right , Overloaded ):
679679 return all (self ._is_subtype (left , item ) for item in right .items )
680680 elif isinstance (right , Instance ):
681- if right .type .is_protocol and right .type .protocol_members == [ "__call__" ] :
682- # OK, a callable can implement a protocol with a single `__call__` member.
681+ if right .type .is_protocol and "__call__" in right .type .protocol_members :
682+ # OK, a callable can implement a protocol with a `__call__` member.
683683 # TODO: we should probably explicitly exclude self-types in this case.
684684 call = find_member ("__call__" , right , left , is_operator = True )
685685 assert call is not None
686686 if self ._is_subtype (left , call ):
687- return True
687+ if len (right .type .protocol_members ) == 1 :
688+ return True
689+ if is_protocol_implementation (left .fallback , right , skip = ["__call__" ]):
690+ return True
688691 if right .type .is_protocol and left .is_type_obj ():
689692 ret_type = get_proper_type (left .ret_type )
690693 if isinstance (ret_type , TupleType ):
@@ -792,12 +795,15 @@ def visit_literal_type(self, left: LiteralType) -> bool:
792795 def visit_overloaded (self , left : Overloaded ) -> bool :
793796 right = self .right
794797 if isinstance (right , Instance ):
795- if right .type .is_protocol and right .type .protocol_members == [ "__call__" ] :
798+ if right .type .is_protocol and "__call__" in right .type .protocol_members :
796799 # same as for CallableType
797800 call = find_member ("__call__" , right , left , is_operator = True )
798801 assert call is not None
799802 if self ._is_subtype (left , call ):
800- return True
803+ if len (right .type .protocol_members ) == 1 :
804+ return True
805+ if is_protocol_implementation (left .fallback , right , skip = ["__call__" ]):
806+ return True
801807 return self ._is_subtype (left .fallback , right )
802808 elif isinstance (right , CallableType ):
803809 for item in left .items :
@@ -938,7 +944,11 @@ def pop_on_exit(stack: list[tuple[T, T]], left: T, right: T) -> Iterator[None]:
938944
939945
940946def is_protocol_implementation (
941- left : Instance , right : Instance , proper_subtype : bool = False , class_obj : bool = False
947+ left : Instance ,
948+ right : Instance ,
949+ proper_subtype : bool = False ,
950+ class_obj : bool = False ,
951+ skip : list [str ] | None = None ,
942952) -> bool :
943953 """Check whether 'left' implements the protocol 'right'.
944954
@@ -958,10 +968,13 @@ def f(self) -> A: ...
958968 as well.
959969 """
960970 assert right .type .is_protocol
971+ if skip is None :
972+ skip = []
961973 # We need to record this check to generate protocol fine-grained dependencies.
962974 TypeState .record_protocol_subtype_check (left .type , right .type )
963975 # nominal subtyping currently ignores '__init__' and '__new__' signatures
964976 members_not_to_check = {"__init__" , "__new__" }
977+ members_not_to_check .update (skip )
965978 # Trivial check that circumvents the bug described in issue 9771:
966979 if left .type .is_protocol :
967980 members_right = set (right .type .protocol_members ) - members_not_to_check
0 commit comments