rustpython_vm/builtins/
weakproxy.rs

1use super::{PyStr, PyStrRef, PyType, PyTypeRef, PyWeak};
2use crate::{
3    atomic_func,
4    class::PyClassImpl,
5    common::hash::PyHash,
6    function::{OptionalArg, PyComparisonValue, PySetterValue},
7    protocol::{PyIter, PyIterReturn, PyMappingMethods, PySequenceMethods},
8    stdlib::builtins::reversed,
9    types::{
10        AsMapping, AsSequence, Comparable, Constructor, GetAttr, Hashable, IterNext, Iterable,
11        PyComparisonOp, Representable, SetAttr,
12    },
13    Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
14};
15use once_cell::sync::Lazy;
16
17#[pyclass(module = false, name = "weakproxy", unhashable = true, traverse)]
18#[derive(Debug)]
19pub struct PyWeakProxy {
20    weak: PyRef<PyWeak>,
21}
22
23impl PyPayload for PyWeakProxy {
24    fn class(ctx: &Context) -> &'static Py<PyType> {
25        ctx.types.weakproxy_type
26    }
27}
28
29#[derive(FromArgs)]
30pub struct WeakProxyNewArgs {
31    #[pyarg(positional)]
32    referent: PyObjectRef,
33    #[pyarg(positional, optional)]
34    callback: OptionalArg<PyObjectRef>,
35}
36
37impl Constructor for PyWeakProxy {
38    type Args = WeakProxyNewArgs;
39
40    fn py_new(
41        cls: PyTypeRef,
42        Self::Args { referent, callback }: Self::Args,
43        vm: &VirtualMachine,
44    ) -> PyResult {
45        // using an internal subclass as the class prevents us from getting the generic weakref,
46        // which would mess up the weakref count
47        let weak_cls = WEAK_SUBCLASS.get_or_init(|| {
48            vm.ctx.new_class(
49                None,
50                "__weakproxy",
51                vm.ctx.types.weakref_type.to_owned(),
52                super::PyWeak::make_slots(),
53            )
54        });
55        // TODO: PyWeakProxy should use the same payload as PyWeak
56        PyWeakProxy {
57            weak: referent.downgrade_with_typ(callback.into_option(), weak_cls.clone(), vm)?,
58        }
59        .into_ref_with_type(vm, cls)
60        .map(Into::into)
61    }
62}
63
64crate::common::static_cell! {
65    static WEAK_SUBCLASS: PyTypeRef;
66}
67
68#[pyclass(with(
69    GetAttr,
70    SetAttr,
71    Constructor,
72    Comparable,
73    AsSequence,
74    AsMapping,
75    Representable,
76    IterNext
77))]
78impl PyWeakProxy {
79    fn try_upgrade(&self, vm: &VirtualMachine) -> PyResult {
80        self.weak.upgrade().ok_or_else(|| new_reference_error(vm))
81    }
82
83    #[pymethod(magic)]
84    fn str(&self, vm: &VirtualMachine) -> PyResult<PyStrRef> {
85        self.try_upgrade(vm)?.str(vm)
86    }
87
88    fn len(&self, vm: &VirtualMachine) -> PyResult<usize> {
89        self.try_upgrade(vm)?.length(vm)
90    }
91
92    #[pymethod(magic)]
93    fn bool(&self, vm: &VirtualMachine) -> PyResult<bool> {
94        self.try_upgrade(vm)?.is_true(vm)
95    }
96
97    #[pymethod(magic)]
98    fn bytes(&self, vm: &VirtualMachine) -> PyResult {
99        self.try_upgrade(vm)?.bytes(vm)
100    }
101
102    #[pymethod(magic)]
103    fn reversed(&self, vm: &VirtualMachine) -> PyResult {
104        let obj = self.try_upgrade(vm)?;
105        reversed(obj, vm)
106    }
107    #[pymethod(magic)]
108    fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
109        self.try_upgrade(vm)?.to_sequence().contains(&needle, vm)
110    }
111
112    fn getitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
113        let obj = self.try_upgrade(vm)?;
114        obj.get_item(&*needle, vm)
115    }
116
117    fn setitem(
118        &self,
119        needle: PyObjectRef,
120        value: PyObjectRef,
121        vm: &VirtualMachine,
122    ) -> PyResult<()> {
123        let obj = self.try_upgrade(vm)?;
124        obj.set_item(&*needle, value, vm)
125    }
126
127    fn delitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
128        let obj = self.try_upgrade(vm)?;
129        obj.del_item(&*needle, vm)
130    }
131}
132
133impl Iterable for PyWeakProxy {
134    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
135        let obj = zelf.try_upgrade(vm)?;
136        Ok(obj.get_iter(vm)?.into())
137    }
138}
139
140impl IterNext for PyWeakProxy {
141    fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
142        let obj = zelf.try_upgrade(vm)?;
143        PyIter::new(obj).next(vm)
144    }
145}
146
147fn new_reference_error(vm: &VirtualMachine) -> PyRef<super::PyBaseException> {
148    vm.new_exception_msg(
149        vm.ctx.exceptions.reference_error.to_owned(),
150        "weakly-referenced object no longer exists".to_owned(),
151    )
152}
153
154impl GetAttr for PyWeakProxy {
155    // TODO: callbacks
156    fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
157        let obj = zelf.try_upgrade(vm)?;
158        obj.get_attr(name, vm)
159    }
160}
161
162impl SetAttr for PyWeakProxy {
163    fn setattro(
164        zelf: &Py<Self>,
165        attr_name: &Py<PyStr>,
166        value: PySetterValue,
167        vm: &VirtualMachine,
168    ) -> PyResult<()> {
169        let obj = zelf.try_upgrade(vm)?;
170        obj.call_set_attr(vm, attr_name, value)
171    }
172}
173
174impl Comparable for PyWeakProxy {
175    fn cmp(
176        zelf: &Py<Self>,
177        other: &PyObject,
178        op: PyComparisonOp,
179        vm: &VirtualMachine,
180    ) -> PyResult<PyComparisonValue> {
181        let obj = zelf.try_upgrade(vm)?;
182        Ok(PyComparisonValue::Implemented(
183            obj.rich_compare_bool(other, op, vm)?,
184        ))
185    }
186}
187
188impl AsSequence for PyWeakProxy {
189    fn as_sequence() -> &'static PySequenceMethods {
190        static AS_SEQUENCE: Lazy<PySequenceMethods> = Lazy::new(|| PySequenceMethods {
191            length: atomic_func!(|seq, vm| PyWeakProxy::sequence_downcast(seq).len(vm)),
192            contains: atomic_func!(|seq, needle, vm| {
193                PyWeakProxy::sequence_downcast(seq).contains(needle.to_owned(), vm)
194            }),
195            ..PySequenceMethods::NOT_IMPLEMENTED
196        });
197        &AS_SEQUENCE
198    }
199}
200
201impl AsMapping for PyWeakProxy {
202    fn as_mapping() -> &'static PyMappingMethods {
203        static AS_MAPPING: PyMappingMethods = PyMappingMethods {
204            length: atomic_func!(|mapping, vm| PyWeakProxy::mapping_downcast(mapping).len(vm)),
205            subscript: atomic_func!(|mapping, needle, vm| {
206                PyWeakProxy::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
207            }),
208            ass_subscript: atomic_func!(|mapping, needle, value, vm| {
209                let zelf = PyWeakProxy::mapping_downcast(mapping);
210                if let Some(value) = value {
211                    zelf.setitem(needle.to_owned(), value, vm)
212                } else {
213                    zelf.delitem(needle.to_owned(), vm)
214                }
215            }),
216        };
217        &AS_MAPPING
218    }
219}
220
221impl Representable for PyWeakProxy {
222    #[inline]
223    fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
224        zelf.try_upgrade(vm)?.repr(vm)
225    }
226
227    #[cold]
228    fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
229        unreachable!("use repr instead")
230    }
231}
232
233pub fn init(context: &Context) {
234    PyWeakProxy::extend_class(context, context.types.weakproxy_type);
235}
236
237impl Hashable for PyWeakProxy {
238    fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
239        zelf.try_upgrade(vm)?.hash(vm)
240    }
241}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.