From 8cee6dc457ea567923a752efe8c4e9a4b97c812b Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Thu, 18 Nov 2021 06:18:12 +0300 Subject: [PATCH] Now we can override attributes with methods, refs #10134 (#11561) Now we can override attributes with methods. Closes #10134 Closes #11556 Co-authored-by: Alex Waygood --- mypy/checker.py | 10 +++++++++ test-data/unit/check-classes.test | 37 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 36751bbc9b15..7189b3d96faf 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1536,6 +1536,16 @@ def check_method_override_for_base_with_name( original_type = self.function_type(original_node) elif isinstance(original_node, Decorator): original_type = self.function_type(original_node.func) + elif isinstance(original_node, Var): + # Super type can define method as an attribute. + # See https://github.com/python/mypy/issues/10134 + + # We also check that sometimes `original_node.type` is None. + # This is the case when we use something like `__hash__ = None`. + if original_node.type is not None: + original_type = get_proper_type(original_node.type) + else: + original_type = NoneType() else: assert False, str(base_attr.node) if isinstance(original_node, (FuncDef, OverloadedFuncDef)): diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 21dae9201c01..b15f9f77f3c7 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -114,6 +114,43 @@ class A: A().f = None # E: Cannot assign to a method +[case testOverrideAttributeWithMethod] +# This was crashing: +# https://github.com/python/mypy/issues/10134 +from typing import Protocol + +class Base: + __hash__ = None + +class Derived(Base): + def __hash__(self) -> int: # E: Signature of "__hash__" incompatible with supertype "Base" + pass + +# Correct: + +class CallableProtocol(Protocol): + def __call__(self, arg: int) -> int: + pass + +class CorrectBase: + attr: CallableProtocol + +class CorrectDerived(CorrectBase): + def attr(self, arg: int) -> int: + pass + +[case testOverrideMethodWithAttribute] +# The reverse should not crash as well: +from typing import Callable + +class Base: + def __hash__(self) -> int: + pass + +class Derived(Base): + __hash__ = 1 # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[Base], int]") + + -- Attributes -- ----------