From 5a3109cfd85a6dc23d39fe275d912793c236e5a5 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 18 Jan 2018 12:32:24 +0000 Subject: [PATCH 1/2] Fix type of forward reference to a decorated class method The case wasn't properly handled before when accessing through a type object -- the type of the forward reference defaulted to `Any`. The fix doesn't work at module top level since module top levels can't be deferred, but the reference would generally fail at runtime anyway. Fixes #4485. --- mypy/checkmember.py | 8 ++++++-- test-data/unit/check-inference.test | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index af2d142335e4..875546154b9b 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -467,8 +467,12 @@ def analyze_class_attribute_access(itype: Instance, return builtin_type('types.ModuleType') if is_decorated: - # TODO: Return type of decorated function. This is quick hack to work around #998. - return AnyType(TypeOfAny.special_form) + assert isinstance(node.node, Decorator) + if node.node.type: + return node.node.type + else: + not_ready_callback(name, context) + return AnyType(TypeOfAny.special_form) else: return function_type(cast(FuncBase, node.node), builtin_type('builtins.function')) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index c9bb0fdb05c9..9cf006548088 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1785,6 +1785,29 @@ y = 0 tmp/m.py:2: error: "int" not callable main:3: error: "int" not callable +[case testForwardReferenceToDecoratedClassMethod] +from typing import TypeVar, Callable + +T = TypeVar('T') +def dec() -> Callable[[T], T]: pass + +A.g # E: Cannot determine type of 'g' + +class A: + @classmethod + def f(cls) -> None: + reveal_type(cls.g) # E: Revealed type is 'def (x: builtins.str)' + + @classmethod + @dec() + def g(cls, x: str) -> None: + pass + + @classmethod + def h(cls) -> None: + reveal_type(cls.g) # E: Revealed type is 'def (x: builtins.str)' +[builtins fixtures/classmethod.pyi] + -- Tests for special cases of unification -- -------------------------------------- From f202bcad9008ff64f4f7d9ddf362c99eae0d17f7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 19 Feb 2018 15:18:04 +0000 Subject: [PATCH 2/2] Update based on review --- mypy/checkmember.py | 2 +- test-data/unit/check-inference.test | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 9c6a74706c9a..fbdc313cff9d 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -472,7 +472,7 @@ def analyze_class_attribute_access(itype: Instance, return node.node.type else: not_ready_callback(name, context) - return AnyType(TypeOfAny.special_form) + return AnyType(TypeOfAny.from_error) else: return function_type(cast(FuncBase, node.node), builtin_type('builtins.function')) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 92f0f7524191..eb43404d81a7 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1807,6 +1807,8 @@ class A: @classmethod def h(cls) -> None: reveal_type(cls.g) # E: Revealed type is 'def (x: builtins.str)' + +reveal_type(A.g) # E: Revealed type is 'def (x: builtins.str)' [builtins fixtures/classmethod.pyi]