rustpython_vm/builtins/
staticmethod.rs

1use super::{PyStr, PyType, PyTypeRef};
2use crate::{
3    class::PyClassImpl,
4    common::lock::PyMutex,
5    function::FuncArgs,
6    types::{Callable, Constructor, GetDescriptor, Initializer, Representable},
7    Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
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    fn class(ctx: &Context) -> &'static Py<PyType> {
18        ctx.types.staticmethod_type
19    }
20}
21
22impl GetDescriptor for PyStaticMethod {
23    fn descr_get(
24        zelf: PyObjectRef,
25        obj: Option<PyObjectRef>,
26        _cls: Option<PyObjectRef>,
27        vm: &VirtualMachine,
28    ) -> PyResult {
29        let (zelf, _obj) = Self::_unwrap(&zelf, obj, vm)?;
30        let x = Ok(zelf.callable.lock().clone());
31        x
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 py_new(cls: PyTypeRef, callable: Self::Args, vm: &VirtualMachine) -> PyResult {
47        let doc = callable.get_attr("__doc__", vm);
48
49        let result = PyStaticMethod {
50            callable: PyMutex::new(callable),
51        }
52        .into_ref_with_type(vm, cls)?;
53        let obj = PyObjectRef::from(result);
54
55        if let Ok(doc) = doc {
56            obj.set_attr("__doc__", doc, vm)?;
57        }
58
59        Ok(obj)
60    }
61}
62
63impl PyStaticMethod {
64    pub fn new_ref(callable: PyObjectRef, ctx: &Context) -> PyRef<Self> {
65        PyRef::new_ref(
66            Self {
67                callable: PyMutex::new(callable),
68            },
69            ctx.types.staticmethod_type.to_owned(),
70            None,
71        )
72    }
73}
74
75impl Initializer for PyStaticMethod {
76    type Args = PyObjectRef;
77
78    fn init(zelf: PyRef<Self>, callable: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
79        *zelf.callable.lock() = callable;
80        Ok(())
81    }
82}
83
84#[pyclass(
85    with(Callable, GetDescriptor, Constructor, Initializer, Representable),
86    flags(BASETYPE, HAS_DICT)
87)]
88impl PyStaticMethod {
89    #[pygetset(magic)]
90    fn func(&self) -> PyObjectRef {
91        self.callable.lock().clone()
92    }
93
94    #[pygetset(magic)]
95    fn wrapped(&self) -> PyObjectRef {
96        self.callable.lock().clone()
97    }
98
99    #[pygetset(magic)]
100    fn module(&self, vm: &VirtualMachine) -> PyResult {
101        self.callable.lock().get_attr("__module__", vm)
102    }
103
104    #[pygetset(magic)]
105    fn qualname(&self, vm: &VirtualMachine) -> PyResult {
106        self.callable.lock().get_attr("__qualname__", vm)
107    }
108
109    #[pygetset(magic)]
110    fn name(&self, vm: &VirtualMachine) -> PyResult {
111        self.callable.lock().get_attr("__name__", vm)
112    }
113
114    #[pygetset(magic)]
115    fn annotations(&self, vm: &VirtualMachine) -> PyResult {
116        self.callable.lock().get_attr("__annotations__", vm)
117    }
118
119    #[pygetset(magic)]
120    fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef {
121        match vm.get_attribute_opt(self.callable.lock().clone(), "__isabstractmethod__") {
122            Ok(Some(is_abstract)) => is_abstract,
123            _ => vm.ctx.new_bool(false).into(),
124        }
125    }
126
127    #[pygetset(magic, setter)]
128    fn set_isabstractmethod(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
129        self.callable
130            .lock()
131            .set_attr("__isabstractmethod__", value, vm)?;
132        Ok(())
133    }
134}
135
136impl Callable for PyStaticMethod {
137    type Args = FuncArgs;
138    #[inline]
139    fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
140        let callable = zelf.callable.lock().clone();
141        callable.call(args, vm)
142    }
143}
144
145impl Representable for PyStaticMethod {
146    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
147        let callable = zelf.callable.lock().repr(vm).unwrap();
148        let class = Self::class(&vm.ctx);
149
150        match (
151            class
152                .qualname(vm)
153                .downcast_ref::<PyStr>()
154                .map(|n| n.as_str()),
155            class.module(vm).downcast_ref::<PyStr>().map(|m| m.as_str()),
156        ) {
157            (None, _) => Err(vm.new_type_error("Unknown qualified name".into())),
158            (Some(qualname), Some(module)) if module != "builtins" => {
159                Ok(format!("<{module}.{qualname}({callable})>"))
160            }
161            _ => Ok(format!("<{}({})>", class.slot_name(), callable)),
162        }
163    }
164}
165
166pub fn init(context: &Context) {
167    PyStaticMethod::extend_class(context, context.types.staticmethod_type);
168}