From cf76cb1ec4af41af0ce085a3eb26f08c3bbced8d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 13 Sep 2018 13:50:59 +0100 Subject: [PATCH 1/6] Add some workarounds to simplify bootstrap --- mypy/checker.py | 13 +++++++++---- mypy/checkmember.py | 6 ++++-- mypy/typeshed | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 0ede0cb5a2bb..e767fef0adc1 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1262,9 +1262,11 @@ def check_method_or_accessor_override_for_base(self, defn: Union[FuncBase, Decor name = defn.name() base_attr = base.names.get(name) if base_attr: + # mypyc hack to workaround mypy misunderstanding multiple inheritance, see #3603 + base_attr_node = base_attr.node # type: Any # 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 (isinstance(base_attr_node, (Var, FuncBase, Decorator)) + and base_attr_node.is_final): self.msg.cant_override_final(name, base.name(), defn) # Second, final can't override anything writeable independently of types. if defn.is_final: @@ -1585,11 +1587,14 @@ def check_compatibility(self, name: str, base1: TypeInfo, if second_type is None: self.msg.cannot_determine_type_in_base(name, base2.name(), ctx) ok = True + # mypyc hack to workaround mypy misunderstanding multiple inheritance, see #3603 + first_node = first.node # type: Any + second_node = second.node # type: Any # 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 isinstance(second_node, (Var, FuncBase, Decorator)) and second_node.is_final: self.msg.cant_override_final(name, base2.name(), ctx) - if isinstance(first.node, (Var, FuncBase, Decorator)) and first.node.is_final: + if isinstance(first_node, (Var, FuncBase, Decorator)) and first_node.is_final: 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..0fc77f4b8136 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1,6 +1,6 @@ """Type checking of attribute access""" -from typing import cast, Callable, List, Optional, TypeVar +from typing import cast, Callable, List, Optional, TypeVar, Any from mypy.types import ( Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike, TypeVarDef, @@ -314,7 +314,9 @@ 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: + # mypyc hack to workaround mypy misunderstanding multiple inheritance, see #3603 + sym_node = sym.node # type: Any + if sym and isinstance(sym_node, (Var, FuncBase, Decorator)) and sym_node.is_final: msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) diff --git a/mypy/typeshed b/mypy/typeshed index c6c8d7e54025..8ed01594456f 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit c6c8d7e54025e0843e4275d21c0fee9e1bdef5e0 +Subproject commit 8ed01594456f3ae58ee987f4f41e9feb2cf32eb1 From 58bab6f29f197b2bec51259610d5ce5ce9c90bc4 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 13 Sep 2018 13:53:16 +0100 Subject: [PATCH 2/6] Bake out typeshed change --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index 8ed01594456f..c6c8d7e54025 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit 8ed01594456f3ae58ee987f4f41e9feb2cf32eb1 +Subproject commit c6c8d7e54025e0843e4275d21c0fee9e1bdef5e0 From f0902c76ff03ec20cfb35f830c5fd9a75dc86aba Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 13 Sep 2018 13:58:41 +0100 Subject: [PATCH 3/6] Fix sym = None in checkmember --- mypy/checkmember.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 0fc77f4b8136..74986871f50f 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -314,10 +314,11 @@ 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) - # mypyc hack to workaround mypy misunderstanding multiple inheritance, see #3603 - sym_node = sym.node # type: Any - if sym and isinstance(sym_node, (Var, FuncBase, Decorator)) and sym_node.is_final: - msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) + if sym: + # mypyc hack to workaround mypy misunderstanding multiple inheritance, see #3603 + sym_node = sym.node # type: Any + if isinstance(sym_node, (Var, FuncBase, Decorator)) and sym_node.is_final: + msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) def analyze_descriptor_access(instance_type: Type, descriptor_type: Type, From 5af3690ddee8516a401060d44637eb90d15aa44e Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 13 Sep 2018 14:10:56 +0100 Subject: [PATCH 4/6] Use a pair of classes instead --- mypy/checker.py | 15 ++++++--------- mypy/checkmember.py | 10 ++++------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e767fef0adc1..747e1056cd72 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1262,11 +1262,9 @@ def check_method_or_accessor_override_for_base(self, defn: Union[FuncBase, Decor name = defn.name() base_attr = base.names.get(name) if base_attr: - # mypyc hack to workaround mypy misunderstanding multiple inheritance, see #3603 - base_attr_node = base_attr.node # type: Any # 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 (isinstance(base_attr.node, (Var, FuncDef, OverloadedFuncDef, Decorator)) + and base_attr.node.is_final): self.msg.cant_override_final(name, base.name(), defn) # Second, final can't override anything writeable independently of types. if defn.is_final: @@ -1587,14 +1585,13 @@ def check_compatibility(self, name: str, base1: TypeInfo, if second_type is None: self.msg.cannot_determine_type_in_base(name, base2.name(), ctx) ok = True - # mypyc hack to workaround mypy misunderstanding multiple inheritance, see #3603 - first_node = first.node # type: Any - second_node = second.node # type: Any # 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 (isinstance(second.node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and + second.node.is_final): self.msg.cant_override_final(name, base2.name(), ctx) - if isinstance(first_node, (Var, FuncBase, Decorator)) and first_node.is_final: + if (isinstance(first.node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and + first.node.is_final): 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 74986871f50f..6d539f8577bd 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1,6 +1,6 @@ """Type checking of attribute access""" -from typing import cast, Callable, List, Optional, TypeVar, Any +from typing import cast, Callable, List, Optional, TypeVar from mypy.types import ( Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike, TypeVarDef, @@ -314,11 +314,9 @@ 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: - # mypyc hack to workaround mypy misunderstanding multiple inheritance, see #3603 - sym_node = sym.node # type: Any - if isinstance(sym_node, (Var, FuncBase, Decorator)) and sym_node.is_final: - msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) + if (sym and isinstance(sym.node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and + sym.node.is_final): + msg.cant_assign_to_final(name, attr_assign=True, ctx=ctx) def analyze_descriptor_access(instance_type: Type, descriptor_type: Type, From 2480bc60eb4c0fac57b647c7dd53426290d51387 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 13 Sep 2018 14:19:45 +0100 Subject: [PATCH 5/6] Use the same pair for previous hack --- mypy/checker.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 747e1056cd72..db392a455c3c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1330,10 +1330,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 From 26ea6146e90bcab59059ba81ccbc01554d84400d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 13 Sep 2018 14:57:08 +0100 Subject: [PATCH 6/6] Refactor final check to a helper --- mypy/checker.py | 11 ++++------- mypy/checkmember.py | 8 ++++++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index db392a455c3c..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, FuncDef, OverloadedFuncDef, 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: @@ -1585,11 +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, FuncDef, OverloadedFuncDef, 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, FuncDef, OverloadedFuncDef, 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 6d539f8577bd..f6a6651f66f3 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -314,8 +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, FuncDef, OverloadedFuncDef, 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) @@ -840,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