mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
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:
1 parent
02917b6d9c
commit
bc495a8b30
2 files changed
+46
-6
No files matched your search
@@ -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,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
Reference in new issue
Block a user