Skip to content

Commit a1203ae

Browse files
morealclaude
andauthored
Improve CPython compatibility related with PyBoundMethod (#7476)
* Add GetDescriptor for PyBoundMethod (return self) CPython's method_descr_get always returns the bound method unchanged. This preserves the original binding when __get__ is called on an already-bound method (e.g. a.meth.__get__(b, B) still returns a). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add constructor validation for PyBoundMethod Reject non-callable functions and None instances, matching CPython's method_new which checks PyCallable_Check(func) and instance != Py_None. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix PyBoundMethod __reduce__ to propagate errors Previously swallowed errors from get_attr with .ok(), silently returning None. Now propagates errors matching CPython's method_reduce. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5b6a479 commit a1203ae

File tree

2 files changed

+31
-7
lines changed

2 files changed

+31
-7
lines changed

Lib/test/test_descr.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5130,7 +5130,6 @@ class Child(Parent):
51305130
gc.collect()
51315131
self.assertEqual(Parent.__subclasses__(), [])
51325132

5133-
@unittest.expectedFailure # TODO: RUSTPYTHON
51345133
def test_instance_method_get_behavior(self):
51355134
# test case for gh-113157
51365135

crates/vm/src/builtins/function.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,17 @@ impl GetAttr for PyBoundMethod {
12161216
}
12171217
}
12181218

1219+
impl GetDescriptor for PyBoundMethod {
1220+
fn descr_get(
1221+
zelf: PyObjectRef,
1222+
_obj: Option<PyObjectRef>,
1223+
_cls: Option<PyObjectRef>,
1224+
_vm: &VirtualMachine,
1225+
) -> PyResult {
1226+
Ok(zelf)
1227+
}
1228+
}
1229+
12191230
#[derive(FromArgs)]
12201231
pub struct PyBoundMethodNewArgs {
12211232
#[pyarg(positional)]
@@ -1230,8 +1241,14 @@ impl Constructor for PyBoundMethod {
12301241
fn py_new(
12311242
_cls: &Py<PyType>,
12321243
Self::Args { function, object }: Self::Args,
1233-
_vm: &VirtualMachine,
1244+
vm: &VirtualMachine,
12341245
) -> PyResult<Self> {
1246+
if !function.is_callable() {
1247+
return Err(vm.new_type_error("first argument must be callable".to_owned()));
1248+
}
1249+
if vm.is_none(&object) {
1250+
return Err(vm.new_type_error("instance must not be None".to_owned()));
1251+
}
12351252
Ok(Self::new(object, function))
12361253
}
12371254
}
@@ -1258,19 +1275,27 @@ impl PyBoundMethod {
12581275
}
12591276

12601277
#[pyclass(
1261-
with(Callable, Comparable, Hashable, GetAttr, Constructor, Representable),
1278+
with(
1279+
Callable,
1280+
Comparable,
1281+
Hashable,
1282+
GetAttr,
1283+
GetDescriptor,
1284+
Constructor,
1285+
Representable
1286+
),
12621287
flags(IMMUTABLETYPE, HAS_WEAKREF)
12631288
)]
12641289
impl PyBoundMethod {
12651290
#[pymethod]
12661291
fn __reduce__(
12671292
&self,
12681293
vm: &VirtualMachine,
1269-
) -> (Option<PyObjectRef>, (PyObjectRef, Option<PyObjectRef>)) {
1270-
let builtins_getattr = vm.builtins.get_attr("getattr", vm).ok();
1294+
) -> PyResult<(PyObjectRef, (PyObjectRef, PyObjectRef))> {
1295+
let builtins_getattr = vm.builtins.get_attr("getattr", vm)?;
12711296
let func_self = self.object.clone();
1272-
let func_name = self.function.get_attr("__name__", vm).ok();
1273-
(builtins_getattr, (func_self, func_name))
1297+
let func_name = self.function.get_attr("__name__", vm)?;
1298+
Ok((builtins_getattr, (func_self, func_name)))
12741299
}
12751300

12761301
#[pygetset]

0 commit comments

Comments
 (0)