From 49213a014b8b869186223f72a5f0fa2a445eee65 Mon Sep 17 00:00:00 2001 From: JiwaniZakir Date: Mon, 18 May 2026 09:19:28 +0000 Subject: [PATCH 1/2] Fix descriptor access for complex generics (fixes #21505) Use the unbound __get__ type and call it with all three arguments (self, instance, owner) so that type inference can jointly solve all type variables. The previous approach called bind_self first and then passed only two arguments, which failed when the __get__ self annotation contained type variables shared with the instance parameter. --- mypy/checkmember.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index b5dcf94a0b206..7948aaf500538 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -710,15 +710,14 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: ) return AnyType(TypeOfAny.from_error) - bound_method = analyze_decorator_or_funcbase_access( - defn=dunder_get, - itype=descriptor_type, - name="__get__", - mx=mx.copy_modified(self_type=descriptor_type), - ) - - typ = map_instance_to_supertype(descriptor_type, dunder_get.info) - dunder_get_type = expand_type_by_instance(bound_method, typ) + # Use the unbound __get__ type and pass (self, instance, owner) together so that + # type inference can jointly solve all type variables. The bound-method approach + # (bind_self first, then call with 2 args) fails when the __get__ self annotation + # contains type variables that appear in the instance parameter too, because + # bind_self may not be able to unify them correctly against complex callable types. + typ = function_type(dunder_get, mx.chk.named_type("builtins.function")) + typ_for_instance = map_instance_to_supertype(descriptor_type, dunder_get.info) + dunder_get_type = expand_type_by_instance(typ, typ_for_instance) if isinstance(instance_type, FunctionLike) and instance_type.is_type_obj(): owner_type = instance_type.items[0].ret_type @@ -734,10 +733,11 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: callable_name, dunder_get_type, [ + TempNode(orig_descriptor_type, context=mx.context), TempNode(instance_type, context=mx.context), TempNode(TypeType.make_normalized(owner_type), context=mx.context), ], - [ARG_POS, ARG_POS], + [ARG_POS, ARG_POS, ARG_POS], mx.context, object_type=descriptor_type, ) @@ -745,10 +745,11 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: _, inferred_dunder_get_type = mx.chk.expr_checker.check_call( dunder_get_type, [ + TempNode(orig_descriptor_type, context=mx.context), TempNode(instance_type, context=mx.context), TempNode(TypeType.make_normalized(owner_type), context=mx.context), ], - [ARG_POS, ARG_POS], + [ARG_POS, ARG_POS, ARG_POS], mx.context, object_type=descriptor_type, callable_name=callable_name, From 9df2b5fde3db414d1759094f02cad060fb37c2a8 Mon Sep 17 00:00:00 2001 From: JiwaniZakir Date: Mon, 18 May 2026 09:23:07 +0000 Subject: [PATCH 2/2] Fix mypy self-check type errors in analyze_descriptor_access get_method returns FuncBase | Decorator; unwrap Decorator.func before passing to function_type (which expects FuncBase). Annotate dunder_get_type as Type so the transform_callee_type (-> Type) assignment is compatible. Fixes #21505 --- mypy/checkmember.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 7948aaf500538..172b97484dc2e 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -715,9 +715,10 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type: # (bind_self first, then call with 2 args) fails when the __get__ self annotation # contains type variables that appear in the instance parameter too, because # bind_self may not be able to unify them correctly against complex callable types. - typ = function_type(dunder_get, mx.chk.named_type("builtins.function")) - typ_for_instance = map_instance_to_supertype(descriptor_type, dunder_get.info) - dunder_get_type = expand_type_by_instance(typ, typ_for_instance) + dunder_get_func = dunder_get.func if isinstance(dunder_get, Decorator) else dunder_get + typ = function_type(dunder_get_func, mx.chk.named_type("builtins.function")) + typ_for_instance = map_instance_to_supertype(descriptor_type, dunder_get_func.info) + dunder_get_type: Type = expand_type_by_instance(typ, typ_for_instance) if isinstance(instance_type, FunctionLike) and instance_type.is_type_obj(): owner_type = instance_type.items[0].ret_type