Skip to main content

rustpython_vm/builtins/
getset.rs

1/*! Python `attribute` descriptor class. (PyGetSet)
2
3*/
4use 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>>, // A class type freed before getset is non-sense.
17    getter: Option<PyGetterFunc>,
18    setter: Option<PySetterFunc>,
19    // doc: Option<String>,
20}
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    // Descriptor methods
102
103    #[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        // Special case for object type
148        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}