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) -> 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. """ concrete = set() # type: Set[str] abstract = [] # type: List[str] abstract_in_this_class = [] # type: 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. 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')