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 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}