diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 3ba99d8e8c6b..bb3f0639d30b 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -936,7 +936,18 @@ def analyze_var( ): # Unwrap nonmember similar to class-level access result = p_result.args[0] - if result and not (implicit or var.info.is_protocol and is_instance_var(var)): + dataclass_metadata = var.info.metadata.get("dataclass", {}) + is_dataclass_field = any( + attribute.get("name") == name for attribute in dataclass_metadata.get("attributes", []) + ) + # A read-only property has already applied descriptor access to its getter + # result. Dataclass fields are represented as synthetic properties, but + # their descriptor access still needs to happen on instance reads. + if ( + result + and not (var.is_property and not var.is_settable_property and not is_dataclass_field) + and not (implicit or var.info.is_protocol and is_instance_var(var)) + ): result = analyze_descriptor_access(result, mx) if hook: result = hook( diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 2cd43c74ebb5..fbc68fd2492c 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -9727,3 +9727,32 @@ def f() -> None: class X: ... undefined # E: Name "undefined" is not defined + +[case testPropertyReturningFunctionType] +from types import FunctionType + +class FuncWrap: + def __init__(self, func: object) -> None: + if not isinstance(func, FunctionType): + raise TypeError() + self._func = func + + @property + def __func__(self) -> FunctionType: + return self._func + +def good() -> int: + return 1 + +wrapped = FuncWrap(good) +assert isinstance(wrapped.__func__, FunctionType) +[builtins fixtures/property-function.pyi] +[file types.pyi] +from typing import final + +@final +class FunctionType: + def __get__(self, instance: object, owner: type | None = ...) -> MethodType: ... + +@final +class MethodType: ... diff --git a/test-data/unit/fixtures/property-function.pyi b/test-data/unit/fixtures/property-function.pyi new file mode 100644 index 000000000000..89eefd891447 --- /dev/null +++ b/test-data/unit/fixtures/property-function.pyi @@ -0,0 +1,22 @@ +from typing import Any + +class object: + def __init__(self) -> None: ... + +class type: + def __init__(self, x: Any) -> None: ... + +class property: + def __init__(self, fget: Any = ...) -> None: ... + +class BaseException: ... +class Exception(BaseException): ... +class TypeError(Exception): ... + +class int: ... +class str: ... +class dict: ... +class tuple: ... +class ellipsis: ... + +def isinstance(x: object, t: Any) -> bool: ...