Problem
Calling a built-in method descriptor's __get__ with only the instance, omitting the owner/type argument that the descriptor protocol allows, raises a TypeError in RustPython. CPython binds the method and returns a builtin_function_or_method.
Measured against CPython 3.14.6:
| call |
RustPython |
CPython |
str.capitalize.__get__("x") |
TypeError: descriptor 'capitalize' needs a type, not 'str', as arg 2 |
<built-in method capitalize of str object at 0x…> |
_io._TextIOBase.read.__get__(io.StringIO()) |
TypeError: descriptor 'read' needs a type, not 'StringIO', as arg 2 |
<built-in method read of _io.StringIO object at 0x…> |
_queue.SimpleQueue.put.__get__(_queue.SimpleQueue()) |
same TypeError |
bound method |
The cause is in PyMethodDescriptor::descr_get (crates/vm/src/builtins/descriptor.rs):
Some(obj) => {
if descr.method.flags.contains(PyMethodFlags::METHOD) {
if cls.is_some_and(|c| c.fast_isinstance(vm.ctx.types.type_type)) {
obj
} else {
return Err(vm.new_type_error(format!(
"descriptor '{}' needs a type, not '{}', as arg 2",
descr.common.name.as_str(),
obj.class().name()
)));
}
} else if descr.method.flags.contains(PyMethodFlags::CLASS) {
obj.class().to_owned().into()
} else {
obj
}
}
A METHOD-flagged descriptor with obj = Some(instance) requires the second argument (cls, the owner) to be a type, and throws an error otherwise. But descr.__get__(obj) omits the owner (cls = None) by design, so the code hits the error branch instead of binding to obj.
This is the root cause of the test_method_descriptor_crash failure in test_types (currently marked @unittest.expectedFailure # TODO: RUSTPYTHON). The test does:
for method, instance in [(_io._TextIOBase.read, io.StringIO()),
(_queue.SimpleQueue.put, _queue.SimpleQueue()),
(str.capitalize, "…")]:
bound = method.__get__(instance) # owner omitted
self.assertIsInstance(bound, types.BuiltinMethodType)
Plan
- In
PyMethodDescriptor::descr_get, treat the owner (cls) as optional for the METHOD-flag branch: when obj is Some and cls is None (the owner is omitted), bind to obj instead of raising. When cls is supplied it must still be a type, so __get__(obj, non_type) keeps raising descriptor '<name>' needs a type, not '<type>', as arg 2.
- Unmark
test_method_descriptor_crash.
Python Documentation or reference to CPython source code
object.__get__(self, instance, owner=None), where the owner argument is optional: https://docs.python.org/3/reference/datamodel.html#object.__get__
- CPython's
method_get (Objects/descrobject.c) returns the descriptor when obj is None; otherwise it binds to obj, and only validates the second argument (must be a type) when one is supplied.
Problem
Calling a built-in method descriptor's
__get__with only the instance, omitting theowner/type argument that the descriptor protocol allows, raises aTypeErrorin RustPython. CPython binds the method and returns abuiltin_function_or_method.Measured against CPython 3.14.6:
str.capitalize.__get__("x")TypeError: descriptor 'capitalize' needs a type, not 'str', as arg 2<built-in method capitalize of str object at 0x…>_io._TextIOBase.read.__get__(io.StringIO())TypeError: descriptor 'read' needs a type, not 'StringIO', as arg 2<built-in method read of _io.StringIO object at 0x…>_queue.SimpleQueue.put.__get__(_queue.SimpleQueue())TypeErrorThe cause is in
PyMethodDescriptor::descr_get(crates/vm/src/builtins/descriptor.rs):A
METHOD-flagged descriptor withobj = Some(instance)requires the second argument (cls, the owner) to be a type, and throws an error otherwise. Butdescr.__get__(obj)omits the owner (cls = None) by design, so the code hits the error branch instead of binding toobj.This is the root cause of the
test_method_descriptor_crashfailure intest_types(currently marked@unittest.expectedFailure # TODO: RUSTPYTHON). The test does:Plan
PyMethodDescriptor::descr_get, treat the owner (cls) as optional for theMETHOD-flag branch: whenobjisSomeandclsisNone(the owner is omitted), bind toobjinstead of raising. Whenclsis supplied it must still be a type, so__get__(obj, non_type)keeps raisingdescriptor '<name>' needs a type, not '<type>', as arg 2.test_method_descriptor_crash.Python Documentation or reference to CPython source code
object.__get__(self, instance, owner=None), where theownerargument is optional: https://docs.python.org/3/reference/datamodel.html#object.__get__method_get(Objects/descrobject.c) returns the descriptor whenobj is None; otherwise it binds toobj, and only validates the second argument (must be a type) when one is supplied.