From 1d8ca01825d1fab834f32486224c4750a0da0004 Mon Sep 17 00:00:00 2001 From: daleselaji-dev <265319989+daleselaji-dev@users.noreply.github.com> Date: Mon, 24 Aug 2026 11:11:51 +0800 Subject: [PATCH 1/3] Fix property access for function types --- mypy/checkmember.py | 6 +++- test-data/unit/check-classes.test | 29 +++++++++++++++++++ test-data/unit/fixtures/property-function.pyi | 22 ++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 test-data/unit/fixtures/property-function.pyi diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 3ba99d8e8c6b2..cc5e543e72847 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -936,7 +936,11 @@ 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)): + if ( + result + and not var.is_property + 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 2cd43c74ebb5b..fbc68fd2492c2 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 0000000000000..89eefd891447e --- /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: ... From 7489125f8eb5c7e92cda862288d995e21c6c24d4 Mon Sep 17 00:00:00 2001 From: daleselaji-dev <265319989+daleselaji-dev@users.noreply.github.com> Date: Mon, 24 Aug 2026 12:39:26 +0800 Subject: [PATCH 2/3] Fix descriptor handling for settable properties --- mypy/checkmember.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index cc5e543e72847..14aff2aabe869 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -936,9 +936,12 @@ def analyze_var( ): # Unwrap nonmember similar to class-level access result = p_result.args[0] + # A read-only property has already applied descriptor access to its getter + # result. Settable properties may represent dataclass-transform descriptors, + # which still require the normal descriptor access on instance reads. if ( result - and not var.is_property + and not (var.is_property and not var.is_settable_property) and not (implicit or var.info.is_protocol and is_instance_var(var)) ): result = analyze_descriptor_access(result, mx) From 3f4c8599b12a34dedca7582160c6e9375ce80f8c Mon Sep 17 00:00:00 2001 From: daleselaji-dev <265319989+daleselaji-dev@users.noreply.github.com> Date: Tue, 25 Aug 2026 10:58:36 +0800 Subject: [PATCH 3/3] Fix descriptor access for dataclass fields --- mypy/checkmember.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 14aff2aabe869..bb3f0639d30b4 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -936,12 +936,16 @@ def analyze_var( ): # Unwrap nonmember similar to class-level access result = p_result.args[0] + 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. Settable properties may represent dataclass-transform descriptors, - # which still require the normal descriptor access on instance reads. + # 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 (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)