From a8c9703cbca9e87750ea7fda359b41b18d9eec30 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:23:37 +0900 Subject: [PATCH] classmethod copy attrs (#5831) --- Lib/test/test_descr.py | 2 -- vm/src/builtins/classmethod.rs | 35 ++++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 10f11414a..af0c3acb6 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1558,8 +1558,6 @@ order (MRO) for bases """ else: self.fail("finding the most derived metaclass should have failed") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_classmethods(self): # Testing class methods... class C(object): diff --git a/vm/src/builtins/classmethod.rs b/vm/src/builtins/classmethod.rs index 94b6e0ca9..42ca28a24 100644 --- a/vm/src/builtins/classmethod.rs +++ b/vm/src/builtins/classmethod.rs @@ -67,19 +67,34 @@ impl Constructor for PyClassMethod { type Args = PyObjectRef; fn py_new(cls: PyTypeRef, callable: Self::Args, vm: &VirtualMachine) -> PyResult { - let doc = callable.get_attr("__doc__", vm); + // Create a dictionary to hold copied attributes + let dict = vm.ctx.new_dict(); - let result = PyClassMethod { + // Copy attributes from the callable to the dict + // This is similar to functools.wraps in CPython + if let Ok(doc) = callable.get_attr("__doc__", vm) { + dict.set_item(identifier!(vm.ctx, __doc__), doc, vm)?; + } + if let Ok(name) = callable.get_attr("__name__", vm) { + dict.set_item(identifier!(vm.ctx, __name__), name, vm)?; + } + if let Ok(qualname) = callable.get_attr("__qualname__", vm) { + dict.set_item(identifier!(vm.ctx, __qualname__), qualname, vm)?; + } + if let Ok(module) = callable.get_attr("__module__", vm) { + dict.set_item(identifier!(vm.ctx, __module__), module, vm)?; + } + if let Ok(annotations) = callable.get_attr("__annotations__", vm) { + dict.set_item(identifier!(vm.ctx, __annotations__), annotations, vm)?; + } + + // Create PyClassMethod instance with the pre-populated dict + let classmethod = PyClassMethod { callable: PyMutex::new(callable), - } - .into_ref_with_type(vm, cls)?; - let obj = PyObjectRef::from(result); + }; - if let Ok(doc) = doc { - obj.set_attr("__doc__", doc, vm)?; - } - - Ok(obj) + let result = PyRef::new_ref(classmethod, cls, Some(dict)); + Ok(PyObjectRef::from(result)) } }