diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 2b0717f181a9..0d26f7da30f2 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Sequence, cast +from typing import TYPE_CHECKING, Callable, Optional, Sequence, cast from mypy import meet, message_registry, subtypes from mypy.erasetype import erase_typevars @@ -776,12 +776,17 @@ def analyze_var( freeze_all_type_vars(t) result: Type = t typ = get_proper_type(typ) - if ( - var.is_initialized_in_class - and (not is_instance_var(var) or mx.is_operator) - and isinstance(typ, FunctionLike) - and not typ.is_type_obj() - ): + + call_type: Optional[ProperType] = None + if var.is_initialized_in_class and (not is_instance_var(var) or mx.is_operator): + if isinstance(typ, FunctionLike) and not typ.is_type_obj(): + call_type = typ + elif var.is_property: + call_type = get_proper_type(_analyze_member_access("__call__", typ, mx)) + else: + call_type = typ + + if isinstance(call_type, FunctionLike) and not call_type.is_type_obj(): if mx.is_lvalue: if var.is_property: if not var.is_settable_property: @@ -792,7 +797,7 @@ def analyze_var( if not var.is_staticmethod: # Class-level function objects and classmethods become bound methods: # the former to the instance, the latter to the class. - functype = typ + functype: FunctionLike = call_type # Use meet to narrow original_type to the dispatched type. # For example, assume # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A) diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index f49541420cc0..4cc523a595d1 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -3158,3 +3158,20 @@ class C(A, B): class D(A, B): def f(self, z: int) -> str: pass # E: Method "f" is not using @override but is overriding a method in class "__main__.A" [typing fixtures/typing-override.pyi] + +[case testCallableProperty] +from typing import Callable + +class something_callable: + def __call__(self, fn) -> str: ... + +def decorator(fn: Callable[..., int]) -> something_callable: ... + +class A: + @property + @decorator + def f(self) -> int: ... + +reveal_type(A.f) # N: Revealed type is "__main__.something_callable" +reveal_type(A().f) # N: Revealed type is "builtins.str" +[builtins fixtures/property.pyi]