rustpython_vm/convert/
try_from.rs1use malachite_bigint::Sign;
2use num_traits::ToPrimitive;
3
4use crate::{
5 Py, VirtualMachine,
6 builtins::PyFloat,
7 object::{AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult},
8};
9
10pub trait TryFromObject: Sized {
15 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self>;
17}
18
19impl<T: for<'a> TryFromBorrowedObject<'a>> TryFromObject for T {
21 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
22 TryFromBorrowedObject::try_from_borrowed_object(vm, &obj)
23 }
24}
25
26impl PyObjectRef {
27 pub fn try_into_value<T>(self, vm: &VirtualMachine) -> PyResult<T>
28 where
29 T: TryFromObject,
30 {
31 T::try_from_object(vm, self)
32 }
33}
34
35impl PyObject {
36 pub fn try_to_value<'a, T>(&'a self, vm: &VirtualMachine) -> PyResult<T>
37 where
38 T: 'a + TryFromBorrowedObject<'a>,
39 {
40 T::try_from_borrowed_object(vm, self)
41 }
42
43 pub fn try_to_ref<'a, T>(&'a self, vm: &VirtualMachine) -> PyResult<&'a Py<T>>
44 where
45 T: 'a + PyPayload,
46 {
47 self.try_to_value::<&Py<T>>(vm)
48 }
49
50 pub fn try_value_with<T, F, R>(&self, f: F, vm: &VirtualMachine) -> PyResult<R>
51 where
52 T: PyPayload,
53 F: Fn(&Py<T>) -> PyResult<R>,
54 {
55 let class = T::class(&vm.ctx);
56 let py_ref = if self.fast_isinstance(class) {
57 self.downcast_ref()
58 .ok_or_else(|| vm.new_downcast_runtime_error(class, self))?
59 } else {
60 return Err(vm.new_downcast_type_error(class, self));
61 };
62 f(py_ref)
63 }
64}
65
66pub trait TryFromBorrowedObject<'a>: Sized
68where
69 Self: 'a,
70{
71 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self>;
73}
74
75impl<T> TryFromObject for PyRef<T>
76where
77 T: PyPayload,
78{
79 #[inline]
80 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
81 let class = T::class(&vm.ctx);
82 if obj.fast_isinstance(class) {
83 T::try_downcast_from(&obj, vm)?;
84 Ok(unsafe { obj.downcast_unchecked() })
85 } else {
86 Err(vm.new_downcast_type_error(class, &obj))
87 }
88 }
89}
90
91impl TryFromObject for PyObjectRef {
92 #[inline]
93 fn try_from_object(_vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
94 Ok(obj)
95 }
96}
97
98impl<T: TryFromObject> TryFromObject for Option<T> {
99 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
100 if vm.is_none(&obj) {
101 Ok(None)
102 } else {
103 T::try_from_object(vm, obj).map(Some)
104 }
105 }
106}
107
108impl<'a, T: 'a + TryFromObject> TryFromBorrowedObject<'a> for Vec<T> {
109 fn try_from_borrowed_object(vm: &VirtualMachine, value: &'a PyObject) -> PyResult<Self> {
110 vm.extract_elements_with(value, |obj| T::try_from_object(vm, obj))
111 }
112}
113
114impl<'a, T: PyPayload> TryFromBorrowedObject<'a> for &'a Py<T> {
115 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
116 let class = T::class(&vm.ctx);
117 if obj.fast_isinstance(class) {
118 obj.downcast_ref()
119 .ok_or_else(|| vm.new_downcast_runtime_error(class, &obj))
120 } else {
121 Err(vm.new_downcast_type_error(class, &obj))
122 }
123 }
124}
125
126impl TryFromObject for core::time::Duration {
127 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
128 if let Some(float) = obj.downcast_ref::<PyFloat>() {
129 let f = float.to_f64();
130
131 if f.is_nan() {
132 return Err(vm.new_value_error("Invalid value NaN (not a number)"));
133 }
134
135 if f < 0.0 {
136 return Err(vm.new_value_error("negative duration"));
137 }
138
139 if !f.is_finite() || f > u64::MAX as f64 {
140 return Err(vm.new_overflow_error("timestamp too large to convert to C PyTime_t"));
141 }
142 let secs = f.trunc() as u64;
144 let frac = f.fract();
145 let nanos = (frac * 1_000_000_000.0).floor() as u32;
147 Ok(Self::new(secs, nanos))
148 } else if let Some(int) = obj.try_index_opt(vm) {
149 let int = int?;
150 let bigint = int.as_bigint();
151 if bigint.sign() == Sign::Minus {
152 return Err(vm.new_value_error("negative duration"));
153 }
154
155 let sec = bigint
156 .to_u64()
157 .ok_or_else(|| vm.new_value_error("value out of range"))?;
158 Ok(Self::from_secs(sec))
159 } else {
160 Err(vm.new_type_error(format!(
161 "expected an int or float for duration, got {}",
162 obj.class()
163 )))
164 }
165 }
166}