Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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__':
Expand Down
7 changes: 6 additions & 1 deletion mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down Expand Up @@ -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