rustpython_vm/builtins/
classmethod.rs1use super::{PyBoundMethod, PyStr, PyType, PyTypeRef};
2use crate::{
3 class::PyClassImpl,
4 common::lock::PyMutex,
5 types::{Constructor, GetDescriptor, Initializer, Representable},
6 AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
7};
8
9#[pyclass(module = false, name = "classmethod")]
30#[derive(Debug)]
31pub struct PyClassMethod {
32 callable: PyMutex<PyObjectRef>,
33}
34
35impl From<PyObjectRef> for PyClassMethod {
36 fn from(callable: PyObjectRef) -> Self {
37 Self {
38 callable: PyMutex::new(callable),
39 }
40 }
41}
42
43impl PyPayload for PyClassMethod {
44 fn class(ctx: &Context) -> &'static Py<PyType> {
45 ctx.types.classmethod_type
46 }
47}
48
49impl GetDescriptor for PyClassMethod {
50 fn descr_get(
51 zelf: PyObjectRef,
52 obj: Option<PyObjectRef>,
53 cls: Option<PyObjectRef>,
54 vm: &VirtualMachine,
55 ) -> PyResult {
56 let (zelf, _obj) = Self::_unwrap(&zelf, obj, vm)?;
57 let cls = cls.unwrap_or_else(|| _obj.class().to_owned().into());
58 let call_descr_get: PyResult<PyObjectRef> = zelf.callable.lock().get_attr("__get__", vm);
59 match call_descr_get {
60 Err(_) => Ok(PyBoundMethod::new_ref(cls, zelf.callable.lock().clone(), &vm.ctx).into()),
61 Ok(call_descr_get) => call_descr_get.call((cls.clone(), cls), vm),
62 }
63 }
64}
65
66impl Constructor for PyClassMethod {
67 type Args = PyObjectRef;
68
69 fn py_new(cls: PyTypeRef, callable: Self::Args, vm: &VirtualMachine) -> PyResult {
70 let doc = callable.get_attr("__doc__", vm);
71
72 let result = PyClassMethod {
73 callable: PyMutex::new(callable),
74 }
75 .into_ref_with_type(vm, cls)?;
76 let obj = PyObjectRef::from(result);
77
78 if let Ok(doc) = doc {
79 obj.set_attr("__doc__", doc, vm)?;
80 }
81
82 Ok(obj)
83 }
84}
85
86impl Initializer for PyClassMethod {
87 type Args = PyObjectRef;
88
89 fn init(zelf: PyRef<Self>, callable: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
90 *zelf.callable.lock() = callable;
91 Ok(())
92 }
93}
94
95impl PyClassMethod {
96 pub fn new_ref(callable: PyObjectRef, ctx: &Context) -> PyRef<Self> {
97 PyRef::new_ref(
98 Self {
99 callable: PyMutex::new(callable),
100 },
101 ctx.types.classmethod_type.to_owned(),
102 None,
103 )
104 }
105}
106
107#[pyclass(
108 with(GetDescriptor, Constructor, Representable),
109 flags(BASETYPE, HAS_DICT)
110)]
111impl PyClassMethod {
112 #[pygetset(magic)]
113 fn func(&self) -> PyObjectRef {
114 self.callable.lock().clone()
115 }
116
117 #[pygetset(magic)]
118 fn wrapped(&self) -> PyObjectRef {
119 self.callable.lock().clone()
120 }
121
122 #[pygetset(magic)]
123 fn module(&self, vm: &VirtualMachine) -> PyResult {
124 self.callable.lock().get_attr("__module__", vm)
125 }
126
127 #[pygetset(magic)]
128 fn qualname(&self, vm: &VirtualMachine) -> PyResult {
129 self.callable.lock().get_attr("__qualname__", vm)
130 }
131
132 #[pygetset(magic)]
133 fn name(&self, vm: &VirtualMachine) -> PyResult {
134 self.callable.lock().get_attr("__name__", vm)
135 }
136
137 #[pygetset(magic)]
138 fn annotations(&self, vm: &VirtualMachine) -> PyResult {
139 self.callable.lock().get_attr("__annotations__", vm)
140 }
141
142 #[pygetset(magic)]
143 fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef {
144 match vm.get_attribute_opt(self.callable.lock().clone(), "__isabstractmethod__") {
145 Ok(Some(is_abstract)) => is_abstract,
146 _ => vm.ctx.new_bool(false).into(),
147 }
148 }
149
150 #[pygetset(magic, setter)]
151 fn set_isabstractmethod(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
152 self.callable
153 .lock()
154 .set_attr("__isabstractmethod__", value, vm)?;
155 Ok(())
156 }
157}
158
159impl Representable for PyClassMethod {
160 #[inline]
161 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
162 let callable = zelf.callable.lock().repr(vm).unwrap();
163 let class = Self::class(&vm.ctx);
164
165 let repr = match (
166 class
167 .qualname(vm)
168 .downcast_ref::<PyStr>()
169 .map(|n| n.as_str()),
170 class.module(vm).downcast_ref::<PyStr>().map(|m| m.as_str()),
171 ) {
172 (None, _) => return Err(vm.new_type_error("Unknown qualified name".into())),
173 (Some(qualname), Some(module)) if module != "builtins" => {
174 format!("<{module}.{qualname}({callable})>")
175 }
176 _ => format!("<{}({})>", class.slot_name(), callable),
177 };
178 Ok(repr)
179 }
180}
181
182pub(crate) fn init(context: &Context) {
183 PyClassMethod::extend_class(context, context.types.classmethod_type);
184}