[red-knot] Fix descriptor __get__ call on class objects (#16304)

## Summary

I spotted a minor mistake in my descriptor protocol implementation where
`C.descriptor` would pass the meta type (`type`) of the type of `C`
(`Literal[C]`) as the owner argument to `__get__`, instead of passing
`Literal[C]` directly.

## Test Plan

New test.
This commit is contained in:
David Peter 2025-02-21 15:35:41 +01:00 committed by GitHub
parent 4dae09ecff
commit 3aa7ba31b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 26 additions and 1 deletions

View file

@ -285,6 +285,31 @@ C.descriptor = "something else"
reveal_type(C.descriptor) # revealed: Unknown | int
```
## `__get__` is called with correct arguments
```py
from __future__ import annotations
class TailoredForClassObjectAccess:
def __get__(self, instance: None, owner: type[C]) -> int:
return 1
class TailoredForInstanceAccess:
def __get__(self, instance: C, owner: type[C] | None = None) -> str:
return "a"
class C:
class_object_access: TailoredForClassObjectAccess = TailoredForClassObjectAccess()
instance_access: TailoredForInstanceAccess = TailoredForInstanceAccess()
reveal_type(C.class_object_access) # revealed: int
reveal_type(C().instance_access) # revealed: str
# TODO: These should emit a diagnostic
reveal_type(C().class_object_access) # revealed: TailoredForClassObjectAccess
reveal_type(C.instance_access) # revealed: TailoredForInstanceAccess
```
## Descriptors with incorrect `__get__` signature
```py

View file

@ -1663,7 +1663,7 @@ impl<'db> Type<'db> {
let member = self.static_member(db, name);
let instance = None;
let owner = self.to_meta_type(db);
let owner = *self;
// TODO: Handle `__get__` call errors (see above).
member.map_type(|ty| ty.try_call_dunder_get(db, instance, owner).unwrap_or(ty))