rustpython_vm/builtins/
getset.rs1use super::{PyType, PyTypeRef};
5use crate::{
6 class::PyClassImpl,
7 function::{IntoPyGetterFunc, IntoPySetterFunc, PyGetterFunc, PySetterFunc, PySetterValue},
8 types::{GetDescriptor, Unconstructible},
9 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
10};
11
12#[pyclass(module = false, name = "getset_descriptor")]
13pub struct PyGetSet {
14 name: String,
15 class: &'static Py<PyType>,
16 getter: Option<PyGetterFunc>,
17 setter: Option<PySetterFunc>,
18 }
20
21impl std::fmt::Debug for PyGetSet {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 write!(
24 f,
25 "PyGetSet {{ name: {}, getter: {}, setter: {} }}",
26 self.name,
27 if self.getter.is_some() {
28 "Some"
29 } else {
30 "None"
31 },
32 if self.setter.is_some() {
33 "Some"
34 } else {
35 "None"
36 },
37 )
38 }
39}
40
41impl PyPayload for PyGetSet {
42 fn class(ctx: &Context) -> &'static Py<PyType> {
43 ctx.types.getset_type
44 }
45}
46
47impl GetDescriptor for PyGetSet {
48 fn descr_get(
49 zelf: PyObjectRef,
50 obj: Option<PyObjectRef>,
51 _cls: Option<PyObjectRef>,
52 vm: &VirtualMachine,
53 ) -> PyResult {
54 let (zelf, obj) = match Self::_check(&zelf, obj, vm) {
55 Some(obj) => obj,
56 None => return Ok(zelf),
57 };
58 if let Some(ref f) = zelf.getter {
59 f(vm, obj)
60 } else {
61 Err(vm.new_attribute_error(format!(
62 "attribute '{}' of '{}' objects is not readable",
63 zelf.name,
64 Self::class(&vm.ctx).name()
65 )))
66 }
67 }
68}
69
70impl PyGetSet {
71 pub fn new(name: String, class: &'static Py<PyType>) -> Self {
72 Self {
73 name,
74 class,
75 getter: None,
76 setter: None,
77 }
78 }
79
80 pub fn with_get<G, X>(mut self, getter: G) -> Self
81 where
82 G: IntoPyGetterFunc<X>,
83 {
84 self.getter = Some(getter.into_getter());
85 self
86 }
87
88 pub fn with_set<S, X>(mut self, setter: S) -> Self
89 where
90 S: IntoPySetterFunc<X>,
91 {
92 self.setter = Some(setter.into_setter());
93 self
94 }
95}
96
97#[pyclass(with(GetDescriptor, Unconstructible))]
98impl PyGetSet {
99 #[pyslot]
102 fn descr_set(
103 zelf: &PyObject,
104 obj: PyObjectRef,
105 value: PySetterValue<PyObjectRef>,
106 vm: &VirtualMachine,
107 ) -> PyResult<()> {
108 let zelf = zelf.try_to_ref::<Self>(vm)?;
109 if let Some(ref f) = zelf.setter {
110 f(vm, obj, value)
111 } else {
112 Err(vm.new_attribute_error(format!(
113 "attribute '{}' of '{}' objects is not writable",
114 zelf.name,
115 obj.class().name()
116 )))
117 }
118 }
119 #[pymethod]
120 fn __set__(
121 zelf: PyObjectRef,
122 obj: PyObjectRef,
123 value: PyObjectRef,
124 vm: &VirtualMachine,
125 ) -> PyResult<()> {
126 Self::descr_set(&zelf, obj, PySetterValue::Assign(value), vm)
127 }
128 #[pymethod]
129 fn __delete__(zelf: PyObjectRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
130 Self::descr_set(&zelf, obj, PySetterValue::Delete, vm)
131 }
132
133 #[pygetset(magic)]
134 fn name(&self) -> String {
135 self.name.clone()
136 }
137
138 #[pygetset(magic)]
139 fn qualname(&self) -> String {
140 format!("{}.{}", self.class.slot_name(), self.name.clone())
141 }
142
143 #[pygetset(magic)]
144 fn objclass(&self) -> PyTypeRef {
145 self.class.to_owned()
146 }
147}
148impl Unconstructible for PyGetSet {}
149
150pub(crate) fn init(context: &Context) {
151 PyGetSet::extend_class(context, context.types.getset_type);
152}