From 03dc44affda12c937aa92fa15fbd80dd3c0d7839 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 16 Nov 2021 16:30:46 +0300 Subject: [PATCH 1/5] Now we can override attributes with methods, refs #10134 --- mypy/checker.py | 6 +++++ test-data/unit/check-classes.test | 37 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 36751bbc9b15d..726fda25088dd 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1536,6 +1536,12 @@ 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`. + original_type = original_node.type or 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 21dae9201c018..b84a13f0a312f 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 Dervied(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 reserse should not crash as well: +from typing import Callable + +class Base: + def __hash__(self) -> int: + pass + +class Dervied(Base): + __hash__ = 1 # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[Base], int]") + + -- Attributes -- ---------- From f55324adf43bf70a599be87bf51f775e24709405 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 16 Nov 2021 16:44:43 +0300 Subject: [PATCH 2/5] Fixes CI --- mypy/checker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 726fda25088dd..7189b3d96fafb 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1539,9 +1539,13 @@ def check_method_override_for_base_with_name( 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`. - original_type = original_node.type or NoneType() + 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)): From 225591957d5dc5c00773cd990f1d4a39fc0a7b8a Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 17 Nov 2021 13:41:09 +0300 Subject: [PATCH 3/5] Update test-data/unit/check-classes.test Co-authored-by: Alex Waygood --- test-data/unit/check-classes.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index b84a13f0a312f..bf1ceb199d882 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -140,7 +140,7 @@ class CorrectDerived(CorrectBase): pass [case testOverrideMethodWithAttribute] -# The reserse should not crash as well: +# The reverse should not crash as well: from typing import Callable class Base: From c9e99ec298a28a3a1e414c4dc92230f6ba5628a9 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 17 Nov 2021 13:41:15 +0300 Subject: [PATCH 4/5] Update test-data/unit/check-classes.test Co-authored-by: Alex Waygood --- test-data/unit/check-classes.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index bf1ceb199d882..6e9d51026c3f7 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -122,7 +122,7 @@ from typing import Protocol class Base: __hash__ = None -class Dervied(Base): +class Derived(Base): def __hash__(self) -> int: # E: Signature of "__hash__" incompatible with supertype "Base" pass From 42411b4001637b77755dc73422b4b32bb7aebdcb Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 17 Nov 2021 13:41:20 +0300 Subject: [PATCH 5/5] Update test-data/unit/check-classes.test Co-authored-by: Alex Waygood --- test-data/unit/check-classes.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 6e9d51026c3f7..b15f9f77f3c7a 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -147,7 +147,7 @@ class Base: def __hash__(self) -> int: pass -class Dervied(Base): +class Derived(Base): __hash__ = 1 # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[Base], int]")