def _combine_protocols(p1: Instance, p2: Instance) -> Instance: def base_repr(base): if 'pfun.Intersection' in base.type.fullname: return ', '.join([repr(b) for b in base.type.bases]) return repr(base) def get_bases(base): if 'pfun.Intersection' in base.type.fullname: bases = set() for b in base.type.bases: bases |= get_bases(b) return bases return set([base]) names = p1.type.names.copy() names.update(p2.type.names) keywords = p1.type.defn.keywords.copy() keywords.update(p2.type.defn.keywords) bases = get_bases(p1) | get_bases(p2) bases_repr = ', '.join(sorted([repr(base) for base in bases])) name = f'Intersection[{bases_repr}]' defn = ClassDef(name, Block([]), p1.type.defn.type_vars + p2.type.defn.type_vars, [NameExpr(p1.type.fullname), NameExpr(p2.type.fullname)], None, list(keywords.items())) defn.fullname = f'pfun.{name}' info = TypeInfo(names, defn, '') info.is_protocol = True info.is_abstract = True info.bases = [p1, p2] info.abstract_attributes = (p1.type.abstract_attributes + p2.type.abstract_attributes) calculate_mro(info) return Instance(info, p1.args + p2.args)
def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool, errors: Errors) -> None: """Calculate abstract status of a class. Set is_abstract of the type to True if the type has an unimplemented abstract attribute. Also compute a list of abstract attributes. Report error is required ABCMeta metaclass is missing. """ concrete = set() # type: Set[str] abstract = [] # type: List[str] abstract_in_this_class = [] # type: List[str] for base in typ.mro: for name, symnode in base.names.items(): node = symnode.node if isinstance(node, OverloadedFuncDef): # Unwrap an overloaded function definition. We can just # check arbitrarily the first overload item. If the # different items have a different abstract status, there # should be an error reported elsewhere. func = node.items[0] # type: Optional[Node] else: func = node if isinstance(func, Decorator): fdef = func.func if fdef.is_abstract and name not in concrete: typ.is_abstract = True abstract.append(name) if base is typ: abstract_in_this_class.append(name) elif isinstance(node, Var): if node.is_abstract_var and name not in concrete: typ.is_abstract = True abstract.append(name) if base is typ: abstract_in_this_class.append(name) concrete.add(name) # In stubs, abstract classes need to be explicitly marked because it is too # easy to accidentally leave a concrete class abstract by forgetting to # implement some methods. typ.abstract_attributes = sorted(abstract) if is_stub_file: if typ.declared_metaclass and typ.declared_metaclass.type.fullname( ) == 'abc.ABCMeta': return if typ.is_protocol: return if abstract and not abstract_in_this_class: def report(message: str, severity: str) -> None: errors.report(typ.line, typ.column, message, severity=severity) attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract)) report( "Class {} has abstract attributes {}".format( typ.fullname(), attrs), 'error') report( "If it is meant to be abstract, add 'abc.ABCMeta' as an explicit metaclass", 'note')
def strip_type_info(self, info: TypeInfo) -> None: info.type_vars = [] info.bases = [] info.abstract_attributes = [] info.mro = [] info.add_type_vars() info.tuple_type = None info.typeddict_type = None info.tuple_type = None info._cache = set() info._cache_proper = set()
def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool, errors: Errors) -> None: """Calculate abstract status of a class. Set is_abstract of the type to True if the type has an unimplemented abstract attribute. Also compute a list of abstract attributes. Report error is required ABCMeta metaclass is missing. """ concrete = set() # type: Set[str] abstract = [] # type: List[str] abstract_in_this_class = [] # type: List[str] for base in typ.mro: for name, symnode in base.names.items(): node = symnode.node if isinstance(node, OverloadedFuncDef): # Unwrap an overloaded function definition. We can just # check arbitrarily the first overload item. If the # different items have a different abstract status, there # should be an error reported elsewhere. func = node.items[0] # type: Optional[Node] else: func = node if isinstance(func, Decorator): fdef = func.func if fdef.is_abstract and name not in concrete: typ.is_abstract = True abstract.append(name) if base is typ: abstract_in_this_class.append(name) elif isinstance(node, Var): if node.is_abstract_var and name not in concrete: typ.is_abstract = True abstract.append(name) if base is typ: abstract_in_this_class.append(name) concrete.add(name) # In stubs, abstract classes need to be explicitly marked because it is too # easy to accidentally leave a concrete class abstract by forgetting to # implement some methods. typ.abstract_attributes = sorted(abstract) if is_stub_file: if typ.declared_metaclass and typ.declared_metaclass.type.fullname() == 'abc.ABCMeta': return if typ.is_protocol: return if abstract and not abstract_in_this_class: def report(message: str, severity: str) -> None: errors.report(typ.line, typ.column, message, severity=severity) attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract)) report("Class {} has abstract attributes {}".format(typ.fullname(), attrs), 'error') report("If it is meant to be abstract, add 'abc.ABCMeta' as an explicit metaclass", 'note')
def strip_type_info(self, info: TypeInfo) -> None: info.type_vars = [] info.bases = [] info.is_abstract = False info.abstract_attributes = [] info.mro = [] info.add_type_vars() info.tuple_type = None info.typeddict_type = None info.tuple_type = None TypeState.reset_subtype_caches_for(info) info.declared_metaclass = None info.metaclass_type = None
def strip_type_info(self, info: TypeInfo) -> None: info.type_vars = [] info.bases = [] info.is_abstract = False info.abstract_attributes = [] info.mro = [] info.add_type_vars() info.tuple_type = None info.typeddict_type = None info.tuple_type = None info._cache = set() info._cache_proper = set() info.declared_metaclass = None info.metaclass_type = None
def strip_type_info(self, info: TypeInfo) -> List[SymbolNode]: info.type_vars = [] info.bases = [] info.is_abstract = False info.abstract_attributes = [] info.mro = [] info.add_type_vars() info.tuple_type = None info.typeddict_type = None info.tuple_type = None TypeState.reset_subtype_caches_for(info) info.declared_metaclass = None info.metaclass_type = None # We need to delete any entries that were generated by plugins, # since they will get regenerated. to_delete = [(k, v) for k, v in info.names.items() if v.plugin_generated] for k, _ in to_delete: del info.names[k] return [v.node for k, v in to_delete if v.node]
def strip_type_info(self, info: TypeInfo) -> List[SymbolNode]: info.type_vars = [] info.bases = [] info.is_abstract = False info.abstract_attributes = [] info.mro = [] info.add_type_vars() info.tuple_type = None info.typeddict_type = None info.tuple_type = None TypeState.reset_subtype_caches_for(info) info.declared_metaclass = None info.metaclass_type = None # We need to delete any entries that were generated by plugins, # since they will get regenerated. to_delete = [(k, v) for k, v in info.names.items() if v.plugin_generated] for k, _ in to_delete: del info.names[k] return [v.node for k, v in to_delete if v.node]
def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool, errors: Errors) -> None: """Calculate abstract status of a class. Set is_abstract of the type to True if the type has an unimplemented abstract attribute. Also compute a list of abstract attributes. Report error is required ABCMeta metaclass is missing. """ if typ.typeddict_type: return # TypedDict can't be abstract concrete: Set[str] = set() abstract: List[str] = [] abstract_in_this_class: List[str] = [] if typ.is_newtype: # Special case: NewTypes are considered as always non-abstract, so they can be used as: # Config = NewType('Config', Mapping[str, str]) # default = Config({'cannot': 'modify'}) # OK typ.abstract_attributes = [] return for base in typ.mro: for name, symnode in base.names.items(): node = symnode.node if isinstance(node, OverloadedFuncDef): # Unwrap an overloaded function definition. We can just # check arbitrarily the first overload item. If the # different items have a different abstract status, there # should be an error reported elsewhere. if node.items: # can be empty for invalid overloads func: Optional[Node] = node.items[0] else: func = None else: func = node if isinstance(func, Decorator): fdef = func.func if fdef.is_abstract and name not in concrete: typ.is_abstract = True abstract.append(name) if base is typ: abstract_in_this_class.append(name) elif isinstance(node, Var): if node.is_abstract_var and name not in concrete: typ.is_abstract = True abstract.append(name) if base is typ: abstract_in_this_class.append(name) concrete.add(name) # In stubs, abstract classes need to be explicitly marked because it is too # easy to accidentally leave a concrete class abstract by forgetting to # implement some methods. typ.abstract_attributes = sorted(abstract) if is_stub_file: if typ.declared_metaclass and typ.declared_metaclass.type.fullname == 'abc.ABCMeta': return if typ.is_protocol: return if abstract and not abstract_in_this_class: def report(message: str, severity: str) -> None: errors.report(typ.line, typ.column, message, severity=severity) attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract)) report("Class {} has abstract attributes {}".format(typ.fullname, attrs), 'error') report("If it is meant to be abstract, add 'abc.ABCMeta' as an explicit metaclass", 'note') if typ.is_final and abstract: attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract)) errors.report(typ.line, typ.column, "Final class {} has abstract attributes {}".format(typ.fullname, attrs))
def visit_instance(self, t: Instance) -> Type: if 'pfun.Intersection' == t.type.fullname: args = [get_proper_type(arg) for arg in t.args] if any(isinstance(arg, AnyType) for arg in args): return AnyType(TypeOfAny.special_form) if all( hasattr(arg, 'type') and arg.type.fullname == 'builtins.object' for arg in args): return args[0] is_typevar = lambda arg: isinstance(arg, TypeVarType) has_type_attr = lambda arg: hasattr(arg, 'type') is_protocol = lambda arg: arg.type.is_protocol is_object = lambda arg: arg.type.fullname == 'builtins.object' if not all( is_typevar(arg) or has_type_attr(arg) and (is_protocol(arg) or is_object(arg)) for arg in args): s = str(t) if self.inferred: msg = (f'All arguments to "Intersection" ' f'must be protocols but inferred "{s}"') else: msg = (f'All arguments to "Intersection" ' f'must be protocols, but got "{s}"') self.api.msg.fail(msg, self.context) return AnyType(TypeOfAny.special_form) if not has_no_typevars(t): return t bases = [] for arg in args: if arg in bases: continue bases.extend(self.get_bases(arg, [])) if len(bases) == 1: return bases[0] bases_repr = ', '.join([repr(base) for base in bases]) name = f'Intersection[{bases_repr}]' defn = ClassDef(name, Block([]), [], [ NameExpr(arg.name) if isinstance(arg, TypeVarType) else NameExpr(arg.type.fullname) for arg in args ], None, []) defn.fullname = f'pfun.{name}' info = TypeInfo({}, defn, '') info.is_protocol = True info.is_abstract = True info.bases = bases attrs = [] for base in bases: if isinstance(base, TypeVarType): continue attrs.extend(base.type.abstract_attributes) info.abstract_attributes = attrs try: calculate_mro(info) except MroError: self.api.msg.fail( 'Cannot determine consistent method resolution ' 'order (MRO) for "%s"' % defn.fullname, self.context) return AnyType(TypeOfAny.special_form) return Instance(info, []) return super().visit_instance(t)