def _report_implementation_problems( self, impl_type: Instance, iface_type: Instance, api: CheckerPluginInterface, context: Context, ) -> None: # This mimicks mypy's MessageBuilder.report_protocol_problems with # simplifications for zope interfaces. # Blatantly assume we are dealing with this particular implementation # of CheckerPluginInterface, because it has functionality we want to # reuse. assert isinstance(api, TypeChecker) impl_info = impl_type.type iface_info = iface_type.type iface_members = self._index_members(iface_info) impl_members = self._index_members(impl_info) # Report missing members missing: Dict[str, List[str]] = defaultdict(list) for member, member_iface in iface_members.items(): if find_member(member, impl_type, impl_type) is None: iface_name = member_iface.fullname if member_iface == iface_info: # Since interface is directly implemented by this class, we # can use shorter name. iface_name = member_iface.name missing[iface_name].append(member) if missing: for iface_name, members in missing.items(): missing_fmt = ", ".join(members) api.fail( f"'{impl_info.name}' is missing following " f"'{iface_name}' interface members: {missing_fmt}.", context, ) # Report member type conflicts for member, member_iface in iface_members.items(): iface_mtype = find_member(member, iface_type, iface_type) assert iface_mtype is not None impl_mtype = find_member(member, impl_type, impl_type) if impl_mtype is None: continue # Find out context for error reporting. If the member is not # defined inside the class that declares interface implementation, # show the error near the class definition. Otherwise, show it near # the member definition. ctx: Context = impl_info impl_member_def_info = impl_members.get(member) impl_name = impl_info.name if impl_member_def_info is not None: if impl_member_def_info == impl_info: ctx = impl_mtype else: impl_name = impl_member_def_info.fullname iface_name = iface_info.name if member_iface != iface_info: iface_name = member_iface.fullname if isinstance(impl_mtype, PartialType): # We don't know how to deal with partial type here. Partial # types will be resolved later when the implementation class is # fully type-checked. We are doing our job before that, so all # we can do is to skip checking of such members. continue if isinstance(iface_mtype, FunctionLike) and isinstance( impl_mtype, FunctionLike): api.check_override( override=impl_mtype, original=iface_mtype, name=impl_name, name_in_super=member, supertype=iface_name, original_class_or_static=False, override_class_or_static=False, node=ctx, ) else: # We could check the field type for compatibility with the code # below, however this yields too many false-positives in # real-life projects. The problem is that we can only check # types defined on a class, instead of instance types, so all # "descriptor" properties will be falsly reported as type # mismatches. Instead we opt-out from property type checking # until we figure out the workaround. # api.check_subtype( # impl_mtype, # iface_mtype, # context=ctx, # subtype_label=f'"{impl_name}" has type', # supertype_label=f'interface "{iface_name}" defines "{member}" as', # ) pass
def unpack_callback_protocol(t: Instance) -> Optional[Type]: assert t.type.is_protocol if t.type.protocol_members == ['__call__']: return find_member('__call__', t, t) return None
def unpack_callback_protocol(t: Instance) -> Optional[Type]: assert t.type.is_protocol if t.type.protocol_members == ['__call__']: return find_member('__call__', t, t) return None