diff --git a/mypy/checker.py b/mypy/checker.py index 0ede0cb5a2bb..22134b1709c5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -37,7 +37,7 @@ import mypy.checkexpr from mypy.checkmember import ( map_type_from_supertype, bind_self, erase_to_bound, type_object_type, - analyze_descriptor_access + analyze_descriptor_access, is_final_node ) from mypy import messages from mypy.subtypes import ( @@ -1263,8 +1263,7 @@ def check_method_or_accessor_override_for_base(self, defn: Union[FuncBase, Decor base_attr = base.names.get(name) if base_attr: # First, check if we override a final (always an error, even with Any types). - if (isinstance(base_attr.node, (Var, FuncBase, Decorator)) - and base_attr.node.is_final): + if is_final_node(base_attr.node): self.msg.cant_override_final(name, base.name(), defn) # Second, final can't override anything writeable independently of types. if defn.is_final: @@ -1330,10 +1329,8 @@ def check_method_override_for_base_with_name( if isinstance(original_type, AnyType) or isinstance(typ, AnyType): pass elif isinstance(original_type, FunctionLike) and isinstance(typ, FunctionLike): - # mypyc hack to workaround mypy misunderstanding multiple inheritance (#3603) - base_attr_node = base_attr.node # type: Any - if (isinstance(base_attr_node, (FuncBase, Decorator)) - and not is_static(base_attr_node)): + if (isinstance(base_attr.node, (FuncDef, OverloadedFuncDef, Decorator)) + and not is_static(base_attr.node)): bound = bind_self(original_type, self.scope.active_self_type()) else: bound = original_type @@ -1587,9 +1584,9 @@ def check_compatibility(self, name: str, base1: TypeInfo, ok = True # Final attributes can never be overridden, but can override # non-final read-only attributes. - if isinstance(second.node, (Var, FuncBase, Decorator)) and second.node.is_final: + if is_final_node(second.node): self.msg.cant_override_final(name, base2.name(), ctx) - if isinstance(first.node, (Var, FuncBase, Decorator)) and first.node.is_final: + if is_final_node(first.node): self.check_no_writable(name, second.node, ctx) # __slots__ is special and the type can vary across class hierarchy. if name == '__slots__': diff --git a/mypy/checkmember.py b/mypy/checkmember.py index a08ba75fddc7..f6a6651f66f3 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -314,7 +314,7 @@ def check_final_member(name: str, info: TypeInfo, msg: MessageBuilder, ctx: Cont """Give an error if the name being assigned was declared as final.""" for base in info.mro: sym = base.names.get(name) - if sym and isinstance(sym.node, (Var, FuncBase, Decorator)) and sym.node.is_final: + if sym and is_final_node(sym.node): msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) @@ -839,3 +839,8 @@ def erase_to_bound(t: Type) -> Type: if isinstance(t.item, TypeVarType): return TypeType.make_normalized(t.item.upper_bound) return t + + +def is_final_node(node: Optional[SymbolNode]) -> bool: + """Check whether `node` corresponds to a final attribute.""" + return isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and node.is_final