PY-63737 Pass the owner class to __get__ on instance access

(cherry picked from commit fb9d4793d2309192ff479af9973b7e97f74c12ab)

GitOrigin-RevId: 74ba94b675a740cfee66c14e3b1ff8dcb92396ca
This commit is contained in:
Daniil Kalinin
2026-07-21 14:09:17 +00:00
committed by intellij-monorepo-bot
parent eece409730
commit fb3797d0d9
2 changed files with 33 additions and 1 deletions
@@ -71,7 +71,7 @@ public final class PyDescriptorTypeUtil {
}
else {
instanceArgumentType = classType;
instanceTypeArgument = noneType;
instanceTypeArgument = classType.toClass();
}
List<PyType> argumentTypes = List.of(instanceArgumentType, instanceTypeArgument);
PyType type = PySyntheticCallHelper.getCallTypeByFunctionName(PyNames.DUNDER_GET, receiverType, argumentTypes, context);
@@ -1188,6 +1188,38 @@ class PyAttributeAndDescriptorTypeTest : PyCodeInsightTestCase() {
# └ TYPE list
""")
@Test
@TestFor(issues = ["PY-63737"])
fun `generic descriptor with own type parameter in get binds the return type variable on instance access`() = test("""
from typing import Callable, TypeVar, Generic
T = TypeVar("T")
T_co = TypeVar("T_co", covariant=True)
class CachedSlotProperty(Generic[T, T_co]):
def __init__(self, f: Callable[[T], T_co]) -> None:
self.f = f
def __get__(self, instance: T, owner: type[T]) -> T_co:
return self.f(instance) + 1
class Foo:
@CachedSlotProperty
def bar(self) -> int:
return 42
expr = Foo().bar
#└ TYPE int
""")
@Test
@TestFor(issues = ["PY-63737"])
fun `instance access passes the owner class so a get typed with type T does not drop other bindings`() = test("""
from typing import Callable
class CachedSlotProperty[T, V]:
def __init__(self, f: Callable[[T], V]) -> None: ...
def __get__(self, instance: T, owner: type[T]) -> V: ...
class Foo:
bar: CachedSlotProperty[Foo, int]
expr = Foo().bar
#└ TYPE int
""")
@Test
@TestFor(issues = ["PY-63737"])
fun `generic descriptor subclass used as decorator accessed on instance`() = test("""