rustpython_vm/builtins/
mappingproxy.rs

1use super::{PyDict, PyDictRef, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef};
2use crate::{
3    atomic_func,
4    class::PyClassImpl,
5    convert::ToPyObject,
6    function::{ArgMapping, OptionalArg, PyComparisonValue},
7    object::{Traverse, TraverseFn},
8    protocol::{PyMapping, PyMappingMethods, PyNumberMethods, PySequenceMethods},
9    types::{
10        AsMapping, AsNumber, AsSequence, Comparable, Constructor, Iterable, PyComparisonOp,
11        Representable,
12    },
13    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
14};
15use once_cell::sync::Lazy;
16
17#[pyclass(module = false, name = "mappingproxy", traverse)]
18#[derive(Debug)]
19pub struct PyMappingProxy {
20    mapping: MappingProxyInner,
21}
22
23#[derive(Debug)]
24enum MappingProxyInner {
25    Class(PyTypeRef),
26    Mapping(ArgMapping),
27}
28
29unsafe impl Traverse for MappingProxyInner {
30    fn traverse(&self, tracer_fn: &mut TraverseFn) {
31        match self {
32            MappingProxyInner::Class(ref r) => r.traverse(tracer_fn),
33            MappingProxyInner::Mapping(ref arg) => arg.traverse(tracer_fn),
34        }
35    }
36}
37
38impl PyPayload for PyMappingProxy {
39    fn class(ctx: &Context) -> &'static Py<PyType> {
40        ctx.types.mappingproxy_type
41    }
42}
43
44impl From<PyTypeRef> for PyMappingProxy {
45    fn from(dict: PyTypeRef) -> Self {
46        Self {
47            mapping: MappingProxyInner::Class(dict),
48        }
49    }
50}
51
52impl From<PyDictRef> for PyMappingProxy {
53    fn from(dict: PyDictRef) -> Self {
54        Self {
55            mapping: MappingProxyInner::Mapping(ArgMapping::from_dict_exact(dict)),
56        }
57    }
58}
59
60impl Constructor for PyMappingProxy {
61    type Args = PyObjectRef;
62
63    fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult {
64        if let Some(methods) = PyMapping::find_methods(&mapping) {
65            if mapping.payload_if_subclass::<PyList>(vm).is_none()
66                && mapping.payload_if_subclass::<PyTuple>(vm).is_none()
67            {
68                return Self {
69                    mapping: MappingProxyInner::Mapping(ArgMapping::with_methods(
70                        mapping,
71                        unsafe { methods.borrow_static() },
72                    )),
73                }
74                .into_ref_with_type(vm, cls)
75                .map(Into::into);
76            }
77        }
78        Err(vm.new_type_error(format!(
79            "mappingproxy() argument must be a mapping, not {}",
80            mapping.class()
81        )))
82    }
83}
84
85#[pyclass(with(
86    AsMapping,
87    Iterable,
88    Constructor,
89    AsSequence,
90    Comparable,
91    AsNumber,
92    Representable
93))]
94impl PyMappingProxy {
95    fn get_inner(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
96        match &self.mapping {
97            MappingProxyInner::Class(class) => Ok(key
98                .as_interned_str(vm)
99                .and_then(|key| class.attributes.read().get(key).cloned())),
100            MappingProxyInner::Mapping(mapping) => mapping.mapping().subscript(&*key, vm).map(Some),
101        }
102    }
103
104    #[pymethod]
105    fn get(
106        &self,
107        key: PyObjectRef,
108        default: OptionalArg,
109        vm: &VirtualMachine,
110    ) -> PyResult<Option<PyObjectRef>> {
111        let obj = self.to_object(vm)?;
112        Ok(Some(vm.call_method(
113            &obj,
114            "get",
115            (key, default.unwrap_or_none(vm)),
116        )?))
117    }
118
119    #[pymethod(magic)]
120    pub fn getitem(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
121        self.get_inner(key.clone(), vm)?
122            .ok_or_else(|| vm.new_key_error(key))
123    }
124
125    fn _contains(&self, key: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
126        match &self.mapping {
127            MappingProxyInner::Class(class) => Ok(key
128                .as_interned_str(vm)
129                .map_or(false, |key| class.attributes.read().contains_key(key))),
130            MappingProxyInner::Mapping(mapping) => mapping.to_sequence().contains(key, vm),
131        }
132    }
133
134    #[pymethod(magic)]
135    pub fn contains(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
136        self._contains(&key, vm)
137    }
138
139    fn to_object(&self, vm: &VirtualMachine) -> PyResult {
140        Ok(match &self.mapping {
141            MappingProxyInner::Mapping(d) => d.as_ref().to_owned(),
142            MappingProxyInner::Class(c) => {
143                PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
144            }
145        })
146    }
147
148    #[pymethod]
149    pub fn items(&self, vm: &VirtualMachine) -> PyResult {
150        let obj = self.to_object(vm)?;
151        vm.call_method(&obj, identifier!(vm, items).as_str(), ())
152    }
153    #[pymethod]
154    pub fn keys(&self, vm: &VirtualMachine) -> PyResult {
155        let obj = self.to_object(vm)?;
156        vm.call_method(&obj, identifier!(vm, keys).as_str(), ())
157    }
158    #[pymethod]
159    pub fn values(&self, vm: &VirtualMachine) -> PyResult {
160        let obj = self.to_object(vm)?;
161        vm.call_method(&obj, identifier!(vm, values).as_str(), ())
162    }
163    #[pymethod]
164    pub fn copy(&self, vm: &VirtualMachine) -> PyResult {
165        match &self.mapping {
166            MappingProxyInner::Mapping(d) => vm.call_method(d, identifier!(vm, copy).as_str(), ()),
167            MappingProxyInner::Class(c) => {
168                Ok(PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm))
169            }
170        }
171    }
172
173    #[pyclassmethod(magic)]
174    fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
175        PyGenericAlias::new(cls, args, vm)
176    }
177
178    #[pymethod(magic)]
179    fn len(&self, vm: &VirtualMachine) -> PyResult<usize> {
180        let obj = self.to_object(vm)?;
181        obj.length(vm)
182    }
183
184    #[pymethod(magic)]
185    fn reversed(&self, vm: &VirtualMachine) -> PyResult {
186        vm.call_method(
187            self.to_object(vm)?.as_object(),
188            identifier!(vm, __reversed__).as_str(),
189            (),
190        )
191    }
192
193    #[pymethod(magic)]
194    fn ior(&self, _args: PyObjectRef, vm: &VirtualMachine) -> PyResult {
195        Err(vm.new_type_error(format!(
196            "\"'|=' is not supported by {}; use '|' instead\"",
197            Self::class(&vm.ctx)
198        )))
199    }
200
201    #[pymethod(name = "__ror__")]
202    #[pymethod(magic)]
203    fn or(&self, args: PyObjectRef, vm: &VirtualMachine) -> PyResult {
204        vm._or(self.copy(vm)?.as_ref(), args.as_ref())
205    }
206}
207
208impl Comparable for PyMappingProxy {
209    fn cmp(
210        zelf: &Py<Self>,
211        other: &PyObject,
212        op: PyComparisonOp,
213        vm: &VirtualMachine,
214    ) -> PyResult<PyComparisonValue> {
215        let obj = zelf.to_object(vm)?;
216        Ok(PyComparisonValue::Implemented(
217            obj.rich_compare_bool(other, op, vm)?,
218        ))
219    }
220}
221
222impl AsMapping for PyMappingProxy {
223    fn as_mapping() -> &'static PyMappingMethods {
224        static AS_MAPPING: Lazy<PyMappingMethods> = Lazy::new(|| PyMappingMethods {
225            length: atomic_func!(|mapping, vm| PyMappingProxy::mapping_downcast(mapping).len(vm)),
226            subscript: atomic_func!(|mapping, needle, vm| {
227                PyMappingProxy::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
228            }),
229            ..PyMappingMethods::NOT_IMPLEMENTED
230        });
231        &AS_MAPPING
232    }
233}
234
235impl AsSequence for PyMappingProxy {
236    fn as_sequence() -> &'static PySequenceMethods {
237        static AS_SEQUENCE: Lazy<PySequenceMethods> = Lazy::new(|| PySequenceMethods {
238            contains: atomic_func!(
239                |seq, target, vm| PyMappingProxy::sequence_downcast(seq)._contains(target, vm)
240            ),
241            ..PySequenceMethods::NOT_IMPLEMENTED
242        });
243        &AS_SEQUENCE
244    }
245}
246
247impl AsNumber for PyMappingProxy {
248    fn as_number() -> &'static PyNumberMethods {
249        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
250            or: Some(|a, b, vm| {
251                if let Some(a) = a.downcast_ref::<PyMappingProxy>() {
252                    a.or(b.to_pyobject(vm), vm)
253                } else {
254                    Ok(vm.ctx.not_implemented())
255                }
256            }),
257            inplace_or: Some(|a, b, vm| {
258                if let Some(a) = a.downcast_ref::<PyMappingProxy>() {
259                    a.ior(b.to_pyobject(vm), vm)
260                } else {
261                    Ok(vm.ctx.not_implemented())
262                }
263            }),
264            ..PyNumberMethods::NOT_IMPLEMENTED
265        };
266        &AS_NUMBER
267    }
268}
269
270impl Iterable for PyMappingProxy {
271    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
272        let obj = zelf.to_object(vm)?;
273        let iter = obj.get_iter(vm)?;
274        Ok(iter.into())
275    }
276}
277
278impl Representable for PyMappingProxy {
279    #[inline]
280    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
281        let obj = zelf.to_object(vm)?;
282        Ok(format!("mappingproxy({})", obj.repr(vm)?))
283    }
284}
285
286pub fn init(context: &Context) {
287    PyMappingProxy::extend_class(context, context.types.mappingproxy_type)
288}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.