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(()), }
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(()), }
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}