Skip to main content

rustpython_vm/builtins/
staticmethod.rs

1use super::{PyGenericAlias, PyStr, PyType, PyTypeRef};
2use crate::{
3    Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
4    class::PyClassImpl,
5    common::lock::PyMutex,
6    function::{FuncArgs, PySetterValue},
7    types::{Callable, Constructor, GetDescriptor, Initializer, Representable},
8};
9
10#[pyclass(module = false, name = "staticmethod", traverse)]
11#[derive(Debug)]
12pub struct PyStaticMethod {
13    pub callable: PyMutex<PyObjectRef>,
14}
15
16impl PyPayload for PyStaticMethod {
17    #[inline]
18    fn class(ctx: &Context) -> &'static Py<PyType> {
19        ctx.types.staticmethod_type
20    }
21}
22
23impl GetDescriptor for PyStaticMethod {
24    fn descr_get(
25        zelf: PyObjectRef,
26        obj: Option<PyObjectRef>,
27        _cls: Option<PyObjectRef>,
28        vm: &VirtualMachine,
29    ) -> PyResult {
30        let (zelf, _obj) = Self::_unwrap(&zelf, obj, vm)?;
31        Ok(zelf.callable.lock().clone())
32    }
33}
34
35impl From<PyObjectRef> for PyStaticMethod {
36    fn from(callable: PyObjectRef) -> Self {
37        Self {
38            callable: PyMutex::new(callable),
39        }
40    }
41}
42
43impl Constructor for PyStaticMethod {
44    type Args = PyObjectRef;
45
46    fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
47        let callable: Self::Args = args.bind(vm)?;
48        let doc = callable.get_attr("__doc__", vm);
49
50        let result = Self {
51            callable: PyMutex::new(callable),
52        }
53        .into_ref_with_type(vm, cls)?;
54        let obj = PyObjectRef::from(result);
55
56        if let Ok(doc) = doc {
57            obj.set_attr("__doc__", doc, vm)?;
58        }
59
60        Ok(obj)
61    }
62
63    fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
64        unimplemented!("use slot_new")
65    }
66}
67
68impl PyStaticMethod {
69    pub fn new(callable: PyObjectRef) -> Self {
70        Self {
71            callable: PyMutex::new(callable),
72        }
73    }
74    #[deprecated(note = "use PyStaticMethod::new(...).into_ref() instead")]
75    pub fn new_ref(callable: PyObjectRef, ctx: &Context) -> PyRef<Self> {
76        Self::new(callable).into_ref(ctx)
77    }
78}
79
80impl Initializer for PyStaticMethod {
81    type Args = PyObjectRef;
82
83    fn init(zelf: PyRef<Self>, callable: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
84        *zelf.callable.lock() = callable;
85        Ok(())
86    }
87}
88
89#[pyclass(
90    with(Callable, GetDescriptor, Constructor, Initializer, Representable),
91    flags(BASETYPE, HAS_DICT, HAS_WEAKREF)
92)]
93impl PyStaticMethod {
94    #[pygetset]
95    fn __func__(&self) -> PyObjectRef {
96        self.callable.lock().clone()
97    }
98
99    #[pygetset]
100    fn __wrapped__(&self) -> PyObjectRef {
101        self.callable.lock().clone()
102    }
103
104    #[pygetset]
105    fn __module__(&self, vm: &VirtualMachine) -> PyResult {
106        self.callable.lock().get_attr("__module__", vm)
107    }
108
109    #[pygetset]
110    fn __qualname__(&self, vm: &VirtualMachine) -> PyResult {
111        self.callable.lock().get_attr("__qualname__", vm)
112    }
113
114    #[pygetset]
115    fn __name__(&self, vm: &VirtualMachine) -> PyResult {
116        self.callable.lock().get_attr("__name__", vm)
117    }
118
119    #[pygetset]
120    fn __annotations__(&self, vm: &VirtualMachine) -> PyResult {
121        self.callable.lock().get_attr("__annotations__", vm)
122    }
123
124    #[pygetset(setter)]
125    fn set___annotations__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
126        match value {
127            PySetterValue::Assign(v) => self.callable.lock().set_attr("__annotations__", v, vm),
128            PySetterValue::Delete => Ok(()), // Silently ignore delete like CPython
129        }
130    }
131
132    #[pygetset]
133    fn __annotate__(&self, vm: &VirtualMachine) -> PyResult {
134        self.callable.lock().get_attr("__annotate__", vm)
135    }
136
137    #[pygetset(setter)]
138    fn set___annotate__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
139        match value {
140            PySetterValue::Assign(v) => self.callable.lock().set_attr("__annotate__", v, vm),
141            PySetterValue::Delete => Ok(()), // Silently ignore delete like CPython
142        }
143    }
144
145    #[pygetset]
146    fn __isabstractmethod__(&self, vm: &VirtualMachine) -> PyObjectRef {
147        match vm.get_attribute_opt(self.callable.lock().clone(), "__isabstractmethod__") {
148            Ok(Some(is_abstract)) => is_abstract,
149            _ => vm.ctx.new_bool(false).into(),
150        }
151    }
152
153    #[pygetset(setter)]
154    fn set___isabstractmethod__(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
155        self.callable
156            .lock()
157            .set_attr("__isabstractmethod__", value, vm)?;
158        Ok(())
159    }
160
161    #[pyclassmethod]
162    fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
163        PyGenericAlias::from_args(cls, args, vm)
164    }
165}
166
167impl Callable for PyStaticMethod {
168    type Args = FuncArgs;
169    #[inline]
170    fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
171        let callable = zelf.callable.lock().clone();
172        callable.call(args, vm)
173    }
174}
175
176impl Representable for PyStaticMethod {
177    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
178        let callable = zelf.callable.lock().repr(vm).unwrap();
179        let class = Self::class(&vm.ctx);
180
181        match (
182            class
183                .__qualname__(vm)
184                .downcast_ref::<PyStr>()
185                .map(|n| n.as_wtf8()),
186            class
187                .__module__(vm)
188                .downcast_ref::<PyStr>()
189                .map(|m| m.as_wtf8()),
190        ) {
191            (None, _) => Err(vm.new_type_error("Unknown qualified name")),
192            (Some(qualname), Some(module)) if module != "builtins" => {
193                Ok(format!("<{module}.{qualname}({callable})>"))
194            }
195            _ => Ok(format!("<{}({})>", class.slot_name(), callable)),
196        }
197    }
198}
199
200pub fn init(context: &'static Context) {
201    PyStaticMethod::extend_class(context, context.types.staticmethod_type);
202}