def visit_instance(self, left: Instance) -> bool: if left.type.fallback_to_any: if isinstance(self.right, NoneTyp): # NOTE: `None` is a *non-subclassable* singleton, therefore no class # can by a subtype of it, even with an `Any` fallback. # This special case is needed to treat descriptors in classes with # dynamic base classes correctly, see #5456. return False return True right = self.right if isinstance(right, TupleType) and right.fallback.type.is_enum: return is_subtype(left, right.fallback) if isinstance(right, Instance): if TypeState.is_cached_subtype_check(left, right): return True # NOTE: left.type.mro may be None in quick mode if there # was an error somewhere. if left.type.mro is not None: for base in left.type.mro: # TODO: Also pass recursively ignore_declared_variance if base._promote and is_subtype( base._promote, self.right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): TypeState.record_subtype_cache_entry(left, right) return True rname = right.type.fullname() # Always try a nominal check if possible, # there might be errors that a user wants to silence *once*. if ((left.type.has_base(rname) or rname == 'builtins.object') and not self.ignore_declared_variance): # Map left type to corresponding right instances. t = map_instance_to_supertype(left, right.type) nominal = all(self.check_type_parameter(lefta, righta, tvar.variance) for lefta, righta, tvar in zip(t.args, right.args, right.type.defn.type_vars)) if nominal: TypeState.record_subtype_cache_entry(left, right) return nominal if right.type.is_protocol and is_protocol_implementation(left, right): return True return False if isinstance(right, TypeType): item = right.item if isinstance(item, TupleType): item = item.fallback if is_named_instance(left, 'builtins.type'): return is_subtype(TypeType(AnyType(TypeOfAny.special_form)), right) if left.type.is_metaclass(): if isinstance(item, AnyType): return True if isinstance(item, Instance): return is_named_instance(item, 'builtins.object') if isinstance(right, CallableType): # Special case: Instance can be a subtype of Callable. call = find_member('__call__', left, left) if call: return is_subtype(call, right) return False else: return False
def is_protocol_implementation(left: Instance, right: Instance, proper_subtype: bool = False) -> bool: """Check whether 'left' implements the protocol 'right'. If 'proper_subtype' is True, then check for a proper subtype. Treat recursive protocols by using the 'assuming' structural subtype matrix (in sparse representation, i.e. as a list of pairs (subtype, supertype)), see also comment in nodes.TypeInfo. When we enter a check for classes (A, P), defined as following:: class P(Protocol): def f(self) -> P: ... class A: def f(self) -> A: ... this results in A being a subtype of P without infinite recursion. On every false result, we pop the assumption, thus avoiding an infinite recursion as well. """ assert right.type.is_protocol # We need to record this check to generate protocol fine-grained dependencies. TypeState.record_protocol_subtype_check(left.type, right.type) assuming = right.type.assuming_proper if proper_subtype else right.type.assuming for (l, r) in reversed(assuming): if sametypes.is_same_type(l, left) and sametypes.is_same_type( r, right): return True with pop_on_exit(assuming, left, right): for member in right.type.protocol_members: # nominal subtyping currently ignores '__init__' and '__new__' signatures if member in ('__init__', '__new__'): continue # The third argument below indicates to what self type is bound. # We always bind self to the subtype. (Similarly to nominal types). supertype = find_member(member, right, left) assert supertype is not None subtype = find_member(member, left, left) # Useful for debugging: # print(member, 'of', left, 'has type', subtype) # print(member, 'of', right, 'has type', supertype) if not subtype: return False if not proper_subtype: # Nominal check currently ignores arg names is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True) else: is_compat = is_proper_subtype(subtype, supertype) if not is_compat: return False if isinstance(subtype, NoneTyp) and isinstance( supertype, CallableType): # We want __hash__ = None idiom to work even without --strict-optional return False subflags = get_member_flags(member, left.type) superflags = get_member_flags(member, right.type) if IS_SETTABLE in superflags: # Check opposite direction for settable attributes. if not is_subtype(supertype, subtype): return False if (IS_CLASSVAR in subflags) != (IS_CLASSVAR in superflags): return False if IS_SETTABLE in superflags and IS_SETTABLE not in subflags: return False # This rule is copied from nominal check in checker.py if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags: return False if proper_subtype: TypeState.record_proper_subtype_cache_entry(left, right) else: TypeState.record_subtype_cache_entry(left, right) return True