rustpython_vm/function/
protocol.rs

1use super::IntoFuncArgs;
2use crate::{
3    builtins::{iter::PySequenceIterator, PyDict, PyDictRef},
4    convert::ToPyObject,
5    identifier,
6    object::{Traverse, TraverseFn},
7    protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods},
8    types::{AsMapping, GenericMethod},
9    AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
10};
11use std::{borrow::Borrow, marker::PhantomData, ops::Deref};
12
13#[derive(Clone, Traverse)]
14pub struct ArgCallable {
15    obj: PyObjectRef,
16    #[pytraverse(skip)]
17    call: GenericMethod,
18}
19
20impl ArgCallable {
21    #[inline(always)]
22    pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
23        let args = args.into_args(vm);
24        (self.call)(&self.obj, args, vm)
25    }
26}
27
28impl std::fmt::Debug for ArgCallable {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.debug_struct("ArgCallable")
31            .field("obj", &self.obj)
32            .field("call", &format!("{:08x}", self.call as usize))
33            .finish()
34    }
35}
36
37impl Borrow<PyObject> for ArgCallable {
38    #[inline(always)]
39    fn borrow(&self) -> &PyObject {
40        &self.obj
41    }
42}
43
44impl AsRef<PyObject> for ArgCallable {
45    #[inline(always)]
46    fn as_ref(&self) -> &PyObject {
47        &self.obj
48    }
49}
50
51impl From<ArgCallable> for PyObjectRef {
52    #[inline(always)]
53    fn from(value: ArgCallable) -> PyObjectRef {
54        value.obj
55    }
56}
57
58impl TryFromObject for ArgCallable {
59    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
60        let Some(callable) = obj.to_callable() else {
61            return Err(
62                vm.new_type_error(format!("'{}' object is not callable", obj.class().name()))
63            );
64        };
65        let call = callable.call;
66        Ok(ArgCallable { obj, call })
67    }
68}
69
70/// An iterable Python object.
71///
72/// `ArgIterable` implements `FromArgs` so that a built-in function can accept
73/// an object that is required to conform to the Python iterator protocol.
74///
75/// ArgIterable can optionally perform type checking and conversions on iterated
76/// objects using a generic type parameter that implements `TryFromObject`.
77pub struct ArgIterable<T = PyObjectRef> {
78    iterable: PyObjectRef,
79    iterfn: Option<crate::types::IterFunc>,
80    _item: PhantomData<T>,
81}
82
83unsafe impl<T: Traverse> Traverse for ArgIterable<T> {
84    fn traverse(&self, tracer_fn: &mut TraverseFn) {
85        self.iterable.traverse(tracer_fn)
86    }
87}
88
89impl<T> ArgIterable<T> {
90    /// Returns an iterator over this sequence of objects.
91    ///
92    /// This operation may fail if an exception is raised while invoking the
93    /// `__iter__` method of the iterable object.
94    pub fn iter<'a>(&self, vm: &'a VirtualMachine) -> PyResult<PyIterIter<'a, T>> {
95        let iter = PyIter::new(match self.iterfn {
96            Some(f) => f(self.iterable.clone(), vm)?,
97            None => PySequenceIterator::new(self.iterable.clone(), vm)?.into_pyobject(vm),
98        });
99        iter.into_iter(vm)
100    }
101}
102
103impl<T> TryFromObject for ArgIterable<T>
104where
105    T: TryFromObject,
106{
107    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
108        let iterfn = {
109            let cls = obj.class();
110            let iterfn = cls.mro_find_map(|x| x.slots.iter.load());
111            if iterfn.is_none() && !cls.has_attr(identifier!(vm, __getitem__)) {
112                return Err(vm.new_type_error(format!("'{}' object is not iterable", cls.name())));
113            }
114            iterfn
115        };
116        Ok(Self {
117            iterable: obj,
118            iterfn,
119            _item: PhantomData,
120        })
121    }
122}
123
124#[derive(Debug, Clone, Traverse)]
125pub struct ArgMapping {
126    obj: PyObjectRef,
127    #[pytraverse(skip)]
128    methods: &'static PyMappingMethods,
129}
130
131impl ArgMapping {
132    #[inline]
133    pub fn with_methods(obj: PyObjectRef, methods: &'static PyMappingMethods) -> Self {
134        Self { obj, methods }
135    }
136
137    #[inline(always)]
138    pub fn from_dict_exact(dict: PyDictRef) -> Self {
139        Self {
140            obj: dict.into(),
141            methods: PyDict::as_mapping(),
142        }
143    }
144
145    #[inline(always)]
146    pub fn mapping(&self) -> PyMapping {
147        PyMapping {
148            obj: &self.obj,
149            methods: self.methods,
150        }
151    }
152}
153
154impl Borrow<PyObject> for ArgMapping {
155    #[inline(always)]
156    fn borrow(&self) -> &PyObject {
157        &self.obj
158    }
159}
160
161impl AsRef<PyObject> for ArgMapping {
162    #[inline(always)]
163    fn as_ref(&self) -> &PyObject {
164        &self.obj
165    }
166}
167
168impl Deref for ArgMapping {
169    type Target = PyObject;
170    #[inline(always)]
171    fn deref(&self) -> &PyObject {
172        &self.obj
173    }
174}
175
176impl From<ArgMapping> for PyObjectRef {
177    #[inline(always)]
178    fn from(value: ArgMapping) -> PyObjectRef {
179        value.obj
180    }
181}
182
183impl ToPyObject for ArgMapping {
184    #[inline(always)]
185    fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
186        self.obj
187    }
188}
189
190impl TryFromObject for ArgMapping {
191    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
192        let mapping = PyMapping::try_protocol(&obj, vm)?;
193        let methods = mapping.methods;
194        Ok(Self { obj, methods })
195    }
196}
197
198// this is not strictly related to PySequence protocol.
199#[derive(Clone)]
200pub struct ArgSequence<T = PyObjectRef>(Vec<T>);
201
202unsafe impl<T: Traverse> Traverse for ArgSequence<T> {
203    fn traverse(&self, tracer_fn: &mut TraverseFn) {
204        self.0.traverse(tracer_fn);
205    }
206}
207
208impl<T> ArgSequence<T> {
209    #[inline(always)]
210    pub fn into_vec(self) -> Vec<T> {
211        self.0
212    }
213    #[inline(always)]
214    pub fn as_slice(&self) -> &[T] {
215        &self.0
216    }
217}
218
219impl<T> std::ops::Deref for ArgSequence<T> {
220    type Target = [T];
221    #[inline(always)]
222    fn deref(&self) -> &[T] {
223        self.as_slice()
224    }
225}
226
227impl<T: TryFromObject> TryFromObject for ArgSequence<T> {
228    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
229        obj.try_to_value(vm).map(Self)
230    }
231}