Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5193,7 +5193,6 @@ def foo(self):
with self.assertRaisesRegex(NotImplementedError, "BAR"):
B().foo

@unittest.expectedFailure # TODO: RUSTPYTHON; Wrong error message
def test_staticmethod_new(self):
class MyStaticMethod(staticmethod):
def __init__(self, func):
Expand All @@ -5204,7 +5203,6 @@ def func(): pass
self.assertIsNone(sm.__func__)
self.assertIsNone(sm.__wrapped__)

@unittest.expectedFailure # TODO: RUSTPYTHON; Wrong error message
def test_classmethod_new(self):
class MyClassMethod(classmethod):
def __init__(self, func):
Expand Down
50 changes: 22 additions & 28 deletions crates/vm/src/builtins/classmethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,34 +66,15 @@ impl Constructor for PyClassMethod {
type Args = PyObjectRef;

fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let callable: Self::Args = args.bind(vm)?;
// Create a dictionary to hold copied attributes
let dict = vm.ctx.new_dict();

// 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
// Validate the signature here, but defer storing the callable and
// copying its attributes to `__init__` so that subclasses overriding
// `__init__` without calling `super().__init__()` see `__func__` as
// `None`, matching CPython.
let _: Self::Args = args.bind(vm)?;
let classmethod = Self {
callable: PyMutex::new(callable),
callable: PyMutex::new(vm.ctx.none()),
};

let result = PyRef::new_ref(classmethod, cls, Some(dict));
let result = PyRef::new_ref(classmethod, cls, Some(vm.ctx.new_dict()));
Ok(PyObjectRef::from(result))
}

Expand All @@ -105,8 +86,21 @@ impl Constructor for PyClassMethod {
impl Initializer for PyClassMethod {
type Args = PyObjectRef;

fn init(zelf: PyRef<Self>, callable: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
*zelf.callable.lock() = callable;
fn init(zelf: PyRef<Self>, callable: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
*zelf.callable.lock() = callable.clone();
// Copy wrapper attributes from the callable, mirroring functools.wraps.
let dict = zelf.as_object().dict().expect("classmethod has __dict__");
for attr in [
identifier!(vm.ctx, __doc__),
identifier!(vm.ctx, __name__),
identifier!(vm.ctx, __qualname__),
identifier!(vm.ctx, __module__),
identifier!(vm.ctx, __annotations__),
] {
if let Ok(value) = callable.get_attr(attr, vm) {
dict.set_item(attr, value, vm)?;
}
}
Ok(())
}
}
Expand Down
27 changes: 13 additions & 14 deletions crates/vm/src/builtins/staticmethod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{PyGenericAlias, PyStr, PyType, PyTypeRef};
use crate::{
Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
class::PyClassImpl,
common::lock::PyMutex,
function::{FuncArgs, PySetterValue},
Expand Down Expand Up @@ -44,20 +44,16 @@ impl Constructor for PyStaticMethod {
type Args = PyObjectRef;

fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
let callable: Self::Args = args.bind(vm)?;
let doc = callable.get_attr("__doc__", vm);

// Validate the signature here, but defer storing the callable and
// copying its attributes to `__init__` so that subclasses overriding
// `__init__` without calling `super().__init__()` see `__func__` as
// `None`, matching CPython.
let _: Self::Args = args.bind(vm)?;
let result = Self {
callable: PyMutex::new(callable),
callable: PyMutex::new(vm.ctx.none()),
}
.into_ref_with_type(vm, cls)?;
let obj = PyObjectRef::from(result);

if let Ok(doc) = doc {
obj.set_attr("__doc__", doc, vm)?;
}

Ok(obj)
Ok(PyObjectRef::from(result))
}

fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
Expand All @@ -80,8 +76,11 @@ impl PyStaticMethod {
impl Initializer for PyStaticMethod {
type Args = PyObjectRef;

fn init(zelf: PyRef<Self>, callable: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
*zelf.callable.lock() = callable;
fn init(zelf: PyRef<Self>, callable: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
*zelf.callable.lock() = callable.clone();
if let Ok(doc) = callable.get_attr("__doc__", vm) {
zelf.as_object().set_attr("__doc__", doc, vm)?;
}
Ok(())
}
}
Expand Down
Loading