Call descriptor __get__ attributes without pre-binding (#9043)

Extract the Python 3.14-compatible descriptor slot dispatch from #8954. Call the raw type attribute with descriptor, instance and owner, preserving ownership and recursion handling. Extend the existing Python snippet for staticmethod, classmethod, callable-object and ordinary __get__ replacements.

Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
youknowdot authored and GitHub committed 2026-10-10 11:56:48 +00:00
1 parent 02917b6d9c
commit bc495a8b30
2 files changed
+46 -6

No files matched your search

+14 -6
View File
@@ -779,13 +779,21 @@ fn descr_get_wrapper(
cls: Option<&PyObject>,
vm: &VirtualMachine,
) -> PyResult {
// A descriptor whose `__get__` is the descriptor itself resolves it by
// fetching `__get__` again, and none of that pushes a Python frame.
// slot_tp_descr_get calls the raw type attribute with all three arguments.
// Binding it first would omit self for builtin functions and incorrectly
// invoke the descriptor protocol of static/class/custom __get__ objects.
// Keep both the callable and self owned across user code that deletes them.
vm.with_recursion(" while calling a Python object", || {
vm.call_special_method(
zelf,
identifier!(vm, __get__),
(obj.map(PyObject::to_owned), cls.map(PyObject::to_owned)),
let Some(get) = zelf.class().get_attr(identifier!(vm, __get__)) else {
return Ok(zelf.to_owned());
};
get.call(
(
zelf.to_owned(),
obj.map(PyObject::to_owned),
cls.map(PyObject::to_owned),
),
vm,
)
})
}
+32
View File
@@ -161,3 +161,35 @@ except RuntimeError as outer:
with assert_raises(ValueError) as caught:
RaisingAttributeHook().absent
assert caught.exception.__context__ is outer
# The descriptor slot calls the raw __get__ attribute without binding it first.
class RawGetDescriptor:
__get__ = staticmethod(lambda *args: args)
class RawGetOwner:
value = RawGetDescriptor()
raw_descriptor = vars(RawGetOwner)["value"]
raw_instance = RawGetOwner()
assert raw_instance.value == (raw_descriptor, raw_instance, RawGetOwner)
assert RawGetOwner.value == (raw_descriptor, None, RawGetOwner)
class CallableGet:
def __get__(self, instance, owner):
raise AssertionError("__get__ must not be bound before the slot call")
def __call__(self, *args):
return args
RawGetDescriptor.__get__ = CallableGet()
assert raw_instance.value == (raw_descriptor, raw_instance, RawGetOwner)
RawGetDescriptor.__get__ = classmethod(lambda *args: args)
with assert_raises(TypeError):
raw_instance.value
RawGetDescriptor.__get__ = lambda self, instance, owner: (self, instance, owner)
assert raw_instance.value == (raw_descriptor, raw_instance, RawGetOwner)