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