rustpython_vm/builtins/
getset.rs1use super::PyType;
4use crate::{
5 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
6 class::PyClassImpl,
7 function::{
8 IntoPyGetterFunc, IntoPySetterFunc, ItemDoc, PyGetterFunc, PySetterFunc, PySetterValue,
9 plain_doc,
10 },
11 object::{Traverse, TraverseFn},
12 types::{GetDescriptor, Representable},
13};
14
15#[pyclass(module = false, name = "getset_descriptor", traverse = "manual")]
16pub struct PyGetSet {
17 #[pymember(name = "__name__")]
18 name: &'static crate::builtins::PyStrInterned,
19 #[pymember(name = "__objclass__")]
22 class: PyRef<PyType>,
23 getter: Option<PyGetterFunc>,
24 setter: Option<PySetterFunc>,
25 doc: ItemDoc,
26}
27
28impl core::fmt::Debug for PyGetSet {
29 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
30 write!(
31 f,
32 "PyGetSet {{ name: {}, getter: {}, setter: {} }}",
33 self.name.as_str(),
34 if self.getter.is_some() {
35 "Some"
36 } else {
37 "None"
38 },
39 if self.setter.is_some() {
40 "Some"
41 } else {
42 "None"
43 },
44 )
45 }
46}
47
48unsafe impl Traverse for PyGetSet {
50 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
51 self.class.traverse(tracer_fn);
52 }
53}
54
55impl PyPayload for PyGetSet {
56 #[inline]
57 fn class(ctx: &Context) -> &'static Py<PyType> {
58 ctx.types.getset_type
59 }
60}
61
62impl GetDescriptor for PyGetSet {
63 fn descr_get(
64 zelf: &PyObject,
65 obj: Option<&PyObject>,
66 _cls: Option<&PyObject>,
67 vm: &VirtualMachine,
68 ) -> PyResult {
69 let (zelf, obj) = match Self::_check(zelf, obj, vm) {
70 Some(obj) => obj,
71 None => return Ok(zelf.to_owned()),
72 };
73 if let Some(ref f) = zelf.getter {
74 f(vm, obj.to_owned())
75 } else {
76 Err(vm.new_attribute_error(format!(
77 "attribute '{}' of '{}' objects is not readable",
78 zelf.name.as_str(),
79 Self::class(&vm.ctx).name()
80 )))
81 }
82 }
83}
84
85impl PyGetSet {
86 #[must_use]
87 pub fn new(name: &str, class: &Py<PyType>, ctx: &Context) -> Self {
88 Self {
89 name: ctx.intern_str(name),
90 class: class.to_owned(),
91 getter: None,
92 setter: None,
93 doc: ItemDoc::NONE,
94 }
95 }
96
97 #[must_use]
98 pub fn with_doc(mut self, doc: ItemDoc) -> Self {
99 self.doc = doc;
100 self
101 }
102
103 #[must_use]
104 pub fn with_get<G, X>(mut self, getter: G) -> Self
105 where
106 G: IntoPyGetterFunc<X>,
107 {
108 self.getter = Some(getter.into_getter());
109 self
110 }
111
112 #[must_use]
113 pub fn with_set<S, X>(mut self, setter: S) -> Self
114 where
115 S: IntoPySetterFunc<X>,
116 {
117 self.setter = Some(setter.into_setter());
118 self
119 }
120}
121
122#[pyclass(flags(DISALLOW_INSTANTIATION), with(GetDescriptor, Representable))]
123impl Py<PyGetSet> {
124 #[pyslot]
127 fn descr_set(
128 zelf: &PyObject,
129 obj: PyObjectRef,
130 value: PySetterValue<PyObjectRef>,
131 vm: &VirtualMachine,
132 ) -> PyResult<()> {
133 let zelf = zelf.try_to_ref::<PyGetSet>(vm)?;
134 if let Some(ref f) = zelf.setter {
135 f(vm, obj, value)
136 } else {
137 Err(vm.new_attribute_error(format!(
138 "attribute '{}' of '{}' objects is not writable",
139 zelf.name.as_str(),
140 obj.class().name()
141 )))
142 }
143 }
144
145 #[pygetset]
146 fn __qualname__(&self) -> String {
147 format!("{}.{}", self.class.slot_name(), self.name.as_str())
148 }
149
150 #[pygetset]
151 fn __doc__(&self) -> Option<&'static str> {
152 plain_doc(self.doc)
153 }
154}
155
156impl Representable for PyGetSet {
157 #[inline]
158 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
159 let class = &zelf.class;
160 if class.is(vm.ctx.types.object_type) {
162 Ok(format!("<attribute '{}'>", zelf.name.as_str()))
163 } else {
164 Ok(format!(
165 "<attribute '{}' of '{}' objects>",
166 zelf.name.as_str(),
167 class.name()
168 ))
169 }
170 }
171}
172
173pub(crate) fn init(context: &'static Context) {
174 PyGetSet::extend_class(context, context.types.getset_type);
175}