Skip to main content

rustpython_vm/builtins/
staticmethod.rs

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