rustpython_vm/convert/
try_from.rs

1use crate::{
2    builtins::PyFloat,
3    object::{AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult},
4    Py, VirtualMachine,
5};
6use num_traits::ToPrimitive;
7
8/// Implemented by any type that can be created from a Python object.
9///
10/// Any type that implements `TryFromObject` is automatically `FromArgs`, and
11/// so can be accepted as a argument to a built-in function.
12pub trait TryFromObject: Sized {
13    /// Attempt to convert a Python object to a value of this type.
14    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self>;
15}
16
17/// Rust-side only version of TryFromObject to reduce unnecessary Rc::clone
18impl<T: for<'a> TryFromBorrowedObject<'a>> TryFromObject for T {
19    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
20        TryFromBorrowedObject::try_from_borrowed_object(vm, &obj)
21    }
22}
23
24impl PyObjectRef {
25    pub fn try_into_value<T>(self, vm: &VirtualMachine) -> PyResult<T>
26    where
27        T: TryFromObject,
28    {
29        T::try_from_object(vm, self)
30    }
31}
32
33impl PyObject {
34    pub fn try_to_value<'a, T>(&'a self, vm: &VirtualMachine) -> PyResult<T>
35    where
36        T: 'a + TryFromBorrowedObject<'a>,
37    {
38        T::try_from_borrowed_object(vm, self)
39    }
40
41    pub fn try_to_ref<'a, T>(&'a self, vm: &VirtualMachine) -> PyResult<&'a Py<T>>
42    where
43        T: 'a + PyPayload,
44    {
45        self.try_to_value::<&Py<T>>(vm)
46    }
47
48    pub fn try_value_with<T, F, R>(&self, f: F, vm: &VirtualMachine) -> PyResult<R>
49    where
50        T: PyPayload,
51        F: Fn(&T) -> PyResult<R>,
52    {
53        let class = T::class(&vm.ctx);
54        let py_ref = if self.fast_isinstance(class) {
55            self.downcast_ref()
56                .ok_or_else(|| vm.new_downcast_runtime_error(class, self))?
57        } else {
58            return Err(vm.new_downcast_type_error(class, self));
59        };
60        f(py_ref)
61    }
62}
63
64/// Lower-cost variation of `TryFromObject`
65pub trait TryFromBorrowedObject<'a>: Sized
66where
67    Self: 'a,
68{
69    /// Attempt to convert a Python object to a value of this type.
70    fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self>;
71}
72
73impl<T> TryFromObject for PyRef<T>
74where
75    T: PyPayload,
76{
77    #[inline]
78    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
79        let class = T::class(&vm.ctx);
80        if obj.fast_isinstance(class) {
81            obj.downcast()
82                .map_err(|obj| vm.new_downcast_runtime_error(class, &obj))
83        } else {
84            Err(vm.new_downcast_type_error(class, &obj))
85        }
86    }
87}
88
89impl TryFromObject for PyObjectRef {
90    #[inline]
91    fn try_from_object(_vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
92        Ok(obj)
93    }
94}
95
96impl<T: TryFromObject> TryFromObject for Option<T> {
97    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
98        if vm.is_none(&obj) {
99            Ok(None)
100        } else {
101            T::try_from_object(vm, obj).map(Some)
102        }
103    }
104}
105
106impl<'a, T: 'a + TryFromObject> TryFromBorrowedObject<'a> for Vec<T> {
107    fn try_from_borrowed_object(vm: &VirtualMachine, value: &'a PyObject) -> PyResult<Self> {
108        vm.extract_elements_with(value, |obj| T::try_from_object(vm, obj))
109    }
110}
111
112impl<'a, T: PyPayload> TryFromBorrowedObject<'a> for &'a Py<T> {
113    fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
114        let class = T::class(&vm.ctx);
115        if obj.fast_isinstance(class) {
116            obj.downcast_ref()
117                .ok_or_else(|| vm.new_downcast_runtime_error(class, &obj))
118        } else {
119            Err(vm.new_downcast_type_error(class, &obj))
120        }
121    }
122}
123
124impl TryFromObject for std::time::Duration {
125    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
126        use std::time::Duration;
127        if let Some(float) = obj.payload::<PyFloat>() {
128            Ok(Duration::from_secs_f64(float.to_f64()))
129        } else if let Some(int) = obj.try_index_opt(vm) {
130            let sec = int?
131                .as_bigint()
132                .to_u64()
133                .ok_or_else(|| vm.new_value_error("value out of range".to_owned()))?;
134            Ok(Duration::from_secs(sec))
135        } else {
136            Err(vm.new_type_error(format!(
137                "expected an int or float for duration, got {}",
138                obj.class()
139            )))
140        }
141    }
142}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.