Skip to main content

rustpython_vm/function/
getset.rs

1//! Python `attribute` descriptor class. (PyGetSet)
2use crate::{
3    Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
4    convert::ToPyResult,
5    function::{BorrowedParam, OwnedParam, RefParam},
6    object::PyThreadingConstraint,
7};
8
9#[derive(result_like::OptionLike, is_macro::Is, Debug)]
10pub enum PySetterValue<T = PyObjectRef> {
11    Assign(T),
12    Delete,
13}
14
15impl PySetterValue {
16    pub fn unwrap_or_none(self, vm: &VirtualMachine) -> PyObjectRef {
17        match self {
18            Self::Assign(value) => value,
19            Self::Delete => vm.ctx.none(),
20        }
21    }
22}
23
24trait FromPySetterValue
25where
26    Self: Sized,
27{
28    fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult<Self>;
29}
30
31impl<T> FromPySetterValue for T
32where
33    T: Sized + TryFromObject,
34{
35    #[inline]
36    fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult<Self> {
37        let obj = obj.ok_or_else(|| vm.new_type_error("can't delete attribute"))?;
38        T::try_from_object(vm, obj)
39    }
40}
41
42impl<T> FromPySetterValue for PySetterValue<T>
43where
44    T: Sized + TryFromObject,
45{
46    #[inline]
47    fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult<Self> {
48        obj.map(|obj| T::try_from_object(vm, obj)).transpose()
49    }
50}
51
52pub(crate) type PyGetterFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine, PyObjectRef) -> PyResult)>;
53pub(crate) type PySetterFunc =
54    Box<py_dyn_fn!(dyn Fn(&VirtualMachine, PyObjectRef, PySetterValue) -> PyResult<()>)>;
55
56pub trait IntoPyGetterFunc<T>: PyThreadingConstraint + Sized + 'static {
57    fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult;
58    fn into_getter(self) -> PyGetterFunc {
59        Box::new(move |vm, obj| self.get(obj, vm))
60    }
61}
62
63impl<F, T, R> IntoPyGetterFunc<(OwnedParam<T>, R, VirtualMachine)> for F
64where
65    F: Fn(T, &VirtualMachine) -> R + 'static + Send + Sync,
66    T: TryFromObject,
67    R: ToPyResult,
68{
69    fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
70        let obj = T::try_from_object(vm, obj)?;
71        (self)(obj, vm).to_pyresult(vm)
72    }
73}
74
75impl<F, S, R> IntoPyGetterFunc<(BorrowedParam<S>, R, VirtualMachine)> for F
76where
77    F: Fn(&Py<S>, &VirtualMachine) -> R + 'static + Send + Sync,
78    S: PyPayload,
79    R: ToPyResult,
80{
81    fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
82        let zelf = PyRef::<S>::try_from_object(vm, obj)?;
83        (self)(&zelf, vm).to_pyresult(vm)
84    }
85}
86
87impl<F, S, R> IntoPyGetterFunc<(RefParam<S>, R, VirtualMachine)> for F
88where
89    F: Fn(&S, &VirtualMachine) -> R + 'static + Send + Sync,
90    S: PyPayload,
91    R: ToPyResult,
92{
93    fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
94        let zelf = PyRef::<S>::try_from_object(vm, obj)?;
95        (self)(&zelf, vm).to_pyresult(vm)
96    }
97}
98
99impl<F, T, R> IntoPyGetterFunc<(OwnedParam<T>, R)> for F
100where
101    F: Fn(T) -> R + 'static + Send + Sync,
102    T: TryFromObject,
103    R: ToPyResult,
104{
105    fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
106        let obj = T::try_from_object(vm, obj)?;
107        (self)(obj).to_pyresult(vm)
108    }
109}
110
111impl<F, S, R> IntoPyGetterFunc<(BorrowedParam<S>, R)> for F
112where
113    F: Fn(&Py<S>) -> R + 'static + Send + Sync,
114    S: PyPayload,
115    R: ToPyResult,
116{
117    fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
118        let zelf = PyRef::<S>::try_from_object(vm, obj)?;
119        (self)(&zelf).to_pyresult(vm)
120    }
121}
122
123impl<F, S, R> IntoPyGetterFunc<(RefParam<S>, R)> for F
124where
125    F: Fn(&S) -> R + 'static + Send + Sync,
126    S: PyPayload,
127    R: ToPyResult,
128{
129    fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
130        let zelf = PyRef::<S>::try_from_object(vm, obj)?;
131        (self)(&zelf).to_pyresult(vm)
132    }
133}
134
135pub trait IntoPyNoResult {
136    fn into_noresult(self) -> PyResult<()>;
137}
138
139impl IntoPyNoResult for () {
140    #[inline]
141    fn into_noresult(self) -> PyResult<()> {
142        Ok(())
143    }
144}
145
146impl IntoPyNoResult for PyResult<()> {
147    #[inline]
148    fn into_noresult(self) -> PyResult<()> {
149        self
150    }
151}
152
153pub trait IntoPySetterFunc<T>: PyThreadingConstraint + Sized + 'static {
154    fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()>;
155    fn into_setter(self) -> PySetterFunc {
156        Box::new(move |vm, obj, value| self.set(obj, value, vm))
157    }
158}
159
160impl<F, T, V, R> IntoPySetterFunc<(OwnedParam<T>, V, R, VirtualMachine)> for F
161where
162    F: Fn(T, V, &VirtualMachine) -> R + 'static + Send + Sync,
163    T: TryFromObject,
164    V: FromPySetterValue,
165    R: IntoPyNoResult,
166{
167    fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
168        let obj = T::try_from_object(vm, obj)?;
169        let value = V::from_setter_value(vm, value)?;
170        (self)(obj, value, vm).into_noresult()
171    }
172}
173
174impl<F, S, V, R> IntoPySetterFunc<(BorrowedParam<S>, V, R, VirtualMachine)> for F
175where
176    F: Fn(&Py<S>, V, &VirtualMachine) -> R + 'static + Send + Sync,
177    S: PyPayload,
178    V: FromPySetterValue,
179    R: IntoPyNoResult,
180{
181    fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
182        let zelf = PyRef::<S>::try_from_object(vm, obj)?;
183        let value = V::from_setter_value(vm, value)?;
184        (self)(&zelf, value, vm).into_noresult()
185    }
186}
187
188impl<F, S, V, R> IntoPySetterFunc<(RefParam<S>, V, R, VirtualMachine)> for F
189where
190    F: Fn(&S, V, &VirtualMachine) -> R + 'static + Send + Sync,
191    S: PyPayload,
192    V: FromPySetterValue,
193    R: IntoPyNoResult,
194{
195    fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
196        let zelf = PyRef::<S>::try_from_object(vm, obj)?;
197        let value = V::from_setter_value(vm, value)?;
198        (self)(&zelf, value, vm).into_noresult()
199    }
200}
201
202impl<F, T, V, R> IntoPySetterFunc<(OwnedParam<T>, V, R)> for F
203where
204    F: Fn(T, V) -> R + 'static + Send + Sync,
205    T: TryFromObject,
206    V: FromPySetterValue,
207    R: IntoPyNoResult,
208{
209    fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
210        let obj = T::try_from_object(vm, obj)?;
211        let value = V::from_setter_value(vm, value)?;
212        (self)(obj, value).into_noresult()
213    }
214}
215
216impl<F, S, V, R> IntoPySetterFunc<(BorrowedParam<S>, V, R)> for F
217where
218    F: Fn(&Py<S>, V) -> R + 'static + Send + Sync,
219    S: PyPayload,
220    V: FromPySetterValue,
221    R: IntoPyNoResult,
222{
223    fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
224        let zelf = PyRef::<S>::try_from_object(vm, obj)?;
225        let value = V::from_setter_value(vm, value)?;
226        (self)(&zelf, value).into_noresult()
227    }
228}
229
230impl<F, S, V, R> IntoPySetterFunc<(RefParam<S>, V, R)> for F
231where
232    F: Fn(&S, V) -> R + 'static + Send + Sync,
233    S: PyPayload,
234    V: FromPySetterValue,
235    R: IntoPyNoResult,
236{
237    fn set(&self, obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
238        let zelf = PyRef::<S>::try_from_object(vm, obj)?;
239        let value = V::from_setter_value(vm, value)?;
240        (self)(&zelf, value).into_noresult()
241    }
242}