rigetti_pyo3/py_try_from/
mod.rs

1// Copyright 2022 Rigetti Computing
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Unifying conversion traits from Python to Rust data.
16
17use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
18
19#[cfg(feature = "time")]
20use pyo3::{
21    exceptions::PyValueError,
22    types::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo},
23};
24
25use pyo3::{
26    types::{
27        PyBool, PyByteArray, PyBytes, PyDict, PyFloat, PyFrozenSet, PyInt, PyList, PySet, PyString,
28    },
29    FromPyObject, IntoPy, Py, PyAny, PyResult, Python,
30};
31
32#[cfg(feature = "complex")]
33/// Conversion trait implementations for complex numbers.
34mod complex;
35
36#[cfg(feature = "time")]
37use crate::datetime::DateTime;
38#[cfg(feature = "time")]
39use pyo3::{types::PyTuple, ToPyObject};
40#[cfg(feature = "time")]
41use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};
42
43#[cfg(feature = "indexmap")]
44/// Conversion trait implementations for [`indexmap`].
45mod indexmap;
46
47/// Convert from a Python type to a Rust type.
48pub trait PyTryFrom<P>: Sized {
49    /// Convert from a `Py<T>`. Defaults to delegating to `py_from_ref`.
50    ///
51    /// # Errors
52    ///
53    /// Any errors that may occur during conversion.
54    fn py_try_from(py: Python, item: &P) -> PyResult<Self>;
55}
56
57impl<P> PyTryFrom<PyAny> for Py<P>
58where
59    Self: for<'a> FromPyObject<'a>,
60{
61    fn py_try_from(_py: Python, item: &PyAny) -> PyResult<Self> {
62        item.extract()
63    }
64}
65
66impl<T, P> PyTryFrom<P> for Box<T>
67where
68    T: PyTryFrom<P>,
69{
70    fn py_try_from(py: Python, item: &P) -> PyResult<Self> {
71        T::py_try_from(py, item).map(Self::new)
72    }
73}
74
75/// Provides a generic implementation of [`PyTryFrom`] for heterogenous tuples of types that themselves implement [`PyTryFrom`]
76macro_rules! impl_py_try_from_tuple {
77    ($($idx:tt $t:tt $p:tt),+) => {
78        impl<$($t,)+ $($p,)+> PyTryFrom<($($p,)+)> for ($($t,)+)
79        where
80            $($t: PyTryFrom<$p>,)+
81        {
82            fn py_try_from(py: Python, item: &($($p,)+)) -> PyResult<Self> {
83                Ok(($(
84                    $t :: py_try_from(py, &item.$idx)?,
85                )+))
86            }
87        }
88    };
89}
90
91// Implement [`PyTryFrom`] for tuples of length 1 to 12, 12 being the maximum arity that [`pyo3::ToPyObject`]
92// is implemented for.
93impl_py_try_from_tuple!(0 T0 P0);
94impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1);
95impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2);
96impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3);
97impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3, 4 T4 P4);
98impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3, 4 T4 P4, 5 T5 P5);
99impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3, 4 T4 P4, 5 T5 P5, 6 T6 P6);
100impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3, 4 T4 P4, 5 T5 P5, 6 T6 P6, 7 T7 P7);
101impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3, 4 T4 P4, 5 T5 P5, 6 T6 P6, 7 T7 P7, 8 T8 P8);
102impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3, 4 T4 P4, 5 T5 P5, 6 T6 P6, 7 T7 P7, 8 T8 P8, 9 T9 P9);
103impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3, 4 T4 P4, 5 T5 P5, 6 T6 P6, 7 T7 P7, 8 T8 P8, 9 T9 P9, 10 T10 P10);
104impl_py_try_from_tuple!(0 T0 P0, 1 T1 P1, 2 T2 P2, 3 T3 P3, 4 T4 P4, 5 T5 P5, 6 T6 P6, 7 T7 P7, 8 T8 P8, 9 T9 P9, 10 T10 P10, 11 T11 P11);
105
106/// Provides a body for `py_try_from`, delegating to the implementation for the given Python type.
107///
108/// This should be used in other macros and for generic/container types that can't be implemented
109/// entirely with a macro.
110#[macro_export]
111macro_rules! private_py_try_from_py_pyany_inner {
112    ($item: ident, $py: ident, $py_type: ty) => {{
113        let actual: &$py_type = $item.extract()?;
114        <Self as $crate::PyTryFrom<$py_type>>::py_try_from($py, actual)
115    }};
116}
117
118/// Generate `PyTryFrom` implementations for `PyAny` that require the `PyAny` to contain a specific Python type.
119#[macro_export]
120macro_rules! private_impl_py_try_from_pyany {
121    ($py_type: ty => $rs_type: ty) => {
122        impl $crate::PyTryFrom<$crate::pyo3::PyAny> for $rs_type {
123            fn py_try_from(
124                py: $crate::pyo3::Python,
125                item: &$crate::pyo3::PyAny,
126            ) -> $crate::pyo3::PyResult<Self> {
127                let actual: &$py_type = item.extract()?;
128                <Self as $crate::PyTryFrom<$py_type>>::py_try_from(py, actual)
129            }
130        }
131    };
132}
133
134/// Implement [`PyTryFrom`] for a given Rust type.
135#[macro_export]
136#[allow(clippy::module_name_repetitions)]
137macro_rules! private_impl_py_try_from {
138    (&$item: ident, $py: ident, $py_type: ty => $rs_type: ty $convert: block) => {
139        #[allow(clippy::use_self)]
140        impl $crate::PyTryFrom<$py_type> for $rs_type {
141            fn py_try_from(
142                $py: $crate::pyo3::Python,
143                $item: &$py_type,
144            ) -> $crate::pyo3::PyResult<Self> {
145                $convert
146            }
147        }
148    };
149}
150pub(crate) use private_impl_py_try_from;
151
152/// Implement [`PyTryFrom<PyAny>`] for a given Rust type by delegating to its implementation
153/// for the given Python type.
154#[macro_export]
155macro_rules! private_impl_py_try_from_with_pyany {
156    (&$item: ident, $py: ident, $py_type: ty => $rs_type: ty $convert: block) => {
157        $crate::private_impl_py_try_from!(&$item, $py, $py_type => $rs_type $convert);
158        $crate::private_impl_py_try_from_pyany!($py_type => $rs_type);
159    };
160}
161pub(crate) use private_impl_py_try_from_with_pyany;
162
163/// Implements [`PyTryFrom<Py<P>>`] for `T` where `T: PyTryFrom<P>` and `P` is a native Python type.
164macro_rules! impl_try_from_py_native {
165    ($py_type: ty => $rs_type: ty) => {
166        $crate::private_impl_py_try_from!(&item, py, $crate::pyo3::Py<$py_type> => $rs_type {
167            let item: &$py_type = item.as_ref(py);
168            <Self as $crate::PyTryFrom<$py_type>>::py_try_from(py, item)
169        });
170    }
171}
172pub(crate) use impl_try_from_py_native;
173
174/// Implements [`PyTryFrom<T>`] for a `T` that is a native Python type.
175macro_rules! impl_try_from_self_python {
176    ($py_type: ty) => {
177        $crate::private_impl_py_try_from!(&item, py, $py_type => $crate::pyo3::Py<$py_type> {
178            Ok(item.into_py(py))
179        });
180        $crate::private_impl_py_try_from!(&item, _py, $crate::pyo3::Py<$py_type> => $crate::pyo3::Py<$py_type> {
181            Ok(item.clone())
182        });
183    }
184}
185pub(crate) use impl_try_from_self_python;
186
187/// Implements [`PyTryFrom<T>`] for a `T` that is a native Rust type.
188macro_rules! impl_try_from_self_rust {
189    ($rs_type: ty) => {
190        private_impl_py_try_from!(&item, _py, $rs_type => $rs_type {
191            Ok(item.clone())
192        });
193    }
194}
195
196/// Implements [`PyTryFrom`] for primitive types by just calling `extract()`.
197macro_rules! impl_try_from_primitive {
198    ($py_type: ty => $rs_type: ty) => {
199        private_impl_py_try_from_with_pyany!(&item, _py, $py_type => $rs_type { item.extract() });
200        impl_try_from_py_native!($py_type => $rs_type);
201    };
202}
203
204// ============ Begin Implementations ==============
205
206// ==== Bool ====
207
208impl_try_from_primitive!(PyBool => bool);
209impl_try_from_self_python!(PyBool);
210impl_try_from_self_rust!(bool);
211
212// ==== ByteArray ====
213
214impl_try_from_self_python!(PyByteArray);
215impl_try_from_py_native!(PyByteArray => Vec<u8>);
216private_impl_py_try_from!(&item, _py, PyByteArray => Vec<u8> {
217    Ok(item.to_vec())
218});
219
220impl_try_from_py_native!(PyByteArray => Box<[u8]>);
221private_impl_py_try_from!(&item, py, PyByteArray => Box<[u8]> {
222    Vec::<u8>::py_try_from(py, item).map(From::from)
223});
224
225// ==== Bytes ====
226
227impl_try_from_self_python!(PyBytes);
228impl_try_from_py_native!(PyBytes => Vec<u8>);
229private_impl_py_try_from!(&item, _py, PyBytes => Vec<u8> {
230    Ok(item.as_bytes().to_vec())
231});
232
233impl_try_from_py_native!(PyBytes => Box<[u8]>);
234private_impl_py_try_from!(&item, py, PyBytes => Box<[u8]> {
235    Vec::<u8>::py_try_from(py, item).map(From::from)
236});
237
238// ==== Date ====
239
240#[cfg(feature = "time")]
241impl_try_from_self_python!(PyDate);
242
243#[cfg(feature = "time")]
244impl_try_from_self_rust!(Date);
245#[cfg(feature = "time")]
246impl_try_from_py_native!(PyDate => Date);
247
248#[cfg(feature = "time")]
249private_impl_py_try_from_with_pyany!(&item, py, PyDate => Date {
250    let year = item.getattr("year").map(|any| i32::py_try_from(py, any))??;
251    let month: u8 = item.getattr("month").map(|any| u8::py_try_from(py, any))??; // 1-12
252    let month: Month = month.try_into()
253        .map_err(|_| {
254            PyValueError::new_err(format!("Expected date month to be within 0-12, got {month}"))
255        })?;
256    let day = item.getattr("day").map(|any| u8::py_try_from(py, any))??; // 1-X
257
258    Self::from_calendar_date(year, month, day).map_err(|err| {
259        PyValueError::new_err(format!("Failed to create Date object: {err}"))
260    })
261});
262
263// ==== DateTime ====
264
265#[cfg(feature = "time")]
266impl_try_from_self_python!(PyDateTime);
267
268#[cfg(feature = "time")]
269impl_try_from_self_rust!(DateTime);
270#[cfg(feature = "time")]
271impl_try_from_py_native!(PyDateTime => DateTime);
272
273#[cfg(feature = "time")]
274private_impl_py_try_from_with_pyany!(&item, py, PyDateTime => DateTime {
275    let date = item.call_method0("date").map(|date| Date::py_try_from(py, date))??;
276    let (time, offset) = item.call_method0("timetz")
277        .map(|time| <(Time, Option<UtcOffset>)>::py_try_from(py, time))??;
278    let datetime = PrimitiveDateTime::new(date, time);
279    let datetime = offset.map_or(Self::Primitive(datetime), |offset| {
280            // Cannot create an OffsetDateTime from parts, for some reason.
281            let datetime = OffsetDateTime::now_utc()
282                .replace_date_time(datetime)
283                .replace_offset(offset);
284            Self::Offset(datetime)
285        });
286
287    Ok(datetime)
288});
289
290// ==== Delta ====
291
292#[cfg(feature = "time")]
293impl_try_from_self_python!(PyDelta);
294
295#[cfg(feature = "time")]
296impl_try_from_self_rust!(Duration);
297
298#[cfg(feature = "time")]
299impl_try_from_py_native!(PyDelta => Duration);
300
301#[cfg(feature = "time")]
302private_impl_py_try_from_with_pyany!(&item, _py, PyDelta => Duration {
303    let days: i64 = item.getattr("days")?.extract()?;
304    let seconds: i64 = item.getattr("seconds")?.extract()?;
305    let microseconds: i32 = item.getattr("microseconds")?.extract()?;
306    let nanoseconds = microseconds.checked_mul(1000).ok_or_else(|| {
307        PyValueError::new_err("Could not fit {microseconds} microseconds as nanoseconds into a 32-bit signed integer")
308    })?;
309    let day_seconds = days.checked_mul(24 * 60 * 60).ok_or_else(|| {
310        PyValueError::new_err("Could not fit {days} days as seconds into a 64-bit signed integer")
311    })?;
312    let seconds = seconds.checked_add(day_seconds).ok_or_else(|| {
313        PyValueError::new_err("Could not add {days} days and {seconds} seconds into a 64-bit signed integer")
314    })?;
315    Ok(Self::new(seconds, nanoseconds))
316});
317
318#[cfg(feature = "time")]
319impl_try_from_self_rust!(std::time::Duration);
320#[cfg(feature = "time")]
321impl_try_from_py_native!(PyDelta => std::time::Duration);
322
323#[cfg(feature = "time")]
324private_impl_py_try_from!(&item, _py, PyDelta => std::time::Duration {
325    let days: u64 = item.getattr("days")?.extract()?;
326    let seconds: u64 = item.getattr("seconds")?.extract()?;
327    let microseconds: u32 = item.getattr("microseconds")?.extract()?;
328    let nanoseconds = microseconds.checked_mul(1000).ok_or_else(|| {
329        PyValueError::new_err("Could not fit {microseconds} microseconds as nanoseconds into a 32-bit signed integer")
330    })?;
331    let day_seconds = days.checked_mul(24 * 60 * 60).ok_or_else(|| {
332        PyValueError::new_err("Could not fit {days} days as seconds into a 64-bit signed integer")
333    })?;
334    let seconds = seconds.checked_add(day_seconds).ok_or_else(|| {
335        PyValueError::new_err("Could not add {days} days and {seconds} seconds into a 64-bit signed integer")
336    })?;
337    Ok(Self::new(seconds, nanoseconds))
338});
339
340// ==== Dict ====
341
342impl_try_from_self_python!(PyDict);
343
344impl<K1, K2, V1, V2, Hasher> PyTryFrom<HashMap<K1, V1>> for HashMap<K2, V2, Hasher>
345where
346    K2: Eq + std::hash::Hash + PyTryFrom<K1>,
347    V2: PyTryFrom<V1>,
348    Hasher: std::hash::BuildHasher + Default,
349{
350    fn py_try_from(py: Python, item: &HashMap<K1, V1>) -> PyResult<Self> {
351        item.iter()
352            .map(|(key, val)| {
353                let key = K2::py_try_from(py, key)?;
354                let val = V2::py_try_from(py, val)?;
355                Ok((key, val))
356            })
357            .collect()
358    }
359}
360
361impl<K, V, Hasher> PyTryFrom<Py<PyDict>> for HashMap<K, V, Hasher>
362where
363    K: Eq + std::hash::Hash + PyTryFrom<PyAny>,
364    V: PyTryFrom<PyAny>,
365    Hasher: std::hash::BuildHasher + Default,
366{
367    fn py_try_from(py: Python, item: &Py<PyDict>) -> PyResult<Self> {
368        Self::py_try_from(py, item.as_ref(py))
369    }
370}
371
372impl<K, V, Hasher> PyTryFrom<PyDict> for HashMap<K, V, Hasher>
373where
374    K: Eq + std::hash::Hash + PyTryFrom<PyAny>,
375    V: PyTryFrom<PyAny>,
376    Hasher: std::hash::BuildHasher + Default,
377{
378    fn py_try_from(py: Python, item: &PyDict) -> PyResult<Self> {
379        let mut map = Self::with_capacity_and_hasher(item.len(), Hasher::default());
380        for (key, val) in item {
381            let key = K::py_try_from(py, key)?;
382            let val = V::py_try_from(py, val)?;
383            map.insert(key, val);
384        }
385        Ok(map)
386    }
387}
388
389impl<K, V, Hasher> PyTryFrom<PyAny> for HashMap<K, V, Hasher>
390where
391    K: Eq + std::hash::Hash + PyTryFrom<PyAny>,
392    V: PyTryFrom<PyAny>,
393    Hasher: std::hash::BuildHasher + Default,
394{
395    fn py_try_from(py: Python, item: &PyAny) -> PyResult<Self> {
396        let dict: &PyDict = item.downcast()?;
397        Self::py_try_from(py, dict)
398    }
399}
400
401impl<K1, K2, V1, V2> PyTryFrom<BTreeMap<K1, V1>> for BTreeMap<K2, V2>
402where
403    K2: Ord + PyTryFrom<K1>,
404    V2: PyTryFrom<V1>,
405{
406    fn py_try_from(py: Python, item: &BTreeMap<K1, V1>) -> PyResult<Self> {
407        item.iter()
408            .map(|(key, val)| {
409                let key = K2::py_try_from(py, key)?;
410                let val = V2::py_try_from(py, val)?;
411                Ok((key, val))
412            })
413            .collect()
414    }
415}
416
417impl<K, V> PyTryFrom<Py<PyDict>> for BTreeMap<K, V>
418where
419    K: Ord + PyTryFrom<PyAny>,
420    V: PyTryFrom<PyAny>,
421{
422    fn py_try_from(py: Python, item: &Py<PyDict>) -> PyResult<Self> {
423        Self::py_try_from(py, item.as_ref(py))
424    }
425}
426
427impl<K, V> PyTryFrom<PyDict> for BTreeMap<K, V>
428where
429    K: Ord + PyTryFrom<PyAny>,
430    V: PyTryFrom<PyAny>,
431{
432    fn py_try_from(py: Python, item: &PyDict) -> PyResult<Self> {
433        let mut map = Self::new();
434        for (key, val) in item {
435            let key = K::py_try_from(py, key)?;
436            let val = V::py_try_from(py, val)?;
437            map.insert(key, val);
438        }
439        Ok(map)
440    }
441}
442
443impl<K, V> PyTryFrom<PyAny> for BTreeMap<K, V>
444where
445    K: Ord + PyTryFrom<PyAny>,
446    V: PyTryFrom<PyAny>,
447{
448    fn py_try_from(py: Python, item: &PyAny) -> PyResult<Self> {
449        let dict: &PyDict = item.downcast()?;
450        <Self as PyTryFrom<PyDict>>::py_try_from(py, dict)
451    }
452}
453
454// ==== Float ====
455
456impl_try_from_self_python!(PyFloat);
457impl_try_from_self_rust!(f32);
458impl_try_from_self_rust!(f64);
459impl_try_from_primitive!(PyFloat => f32);
460impl_try_from_primitive!(PyFloat => f64);
461
462// ==== FrozenSet ====
463
464impl_try_from_self_python!(PyFrozenSet);
465
466impl<T, Hasher> PyTryFrom<Py<PyFrozenSet>> for HashSet<T, Hasher>
467where
468    T: Eq + std::hash::Hash + PyTryFrom<PyAny>,
469    Hasher: std::hash::BuildHasher + Default,
470{
471    fn py_try_from(py: Python, set: &Py<PyFrozenSet>) -> PyResult<Self> {
472        Self::py_try_from(py, set.as_ref(py))
473    }
474}
475
476impl<T, Hasher> PyTryFrom<PyFrozenSet> for HashSet<T, Hasher>
477where
478    T: Eq + std::hash::Hash + PyTryFrom<PyAny>,
479    Hasher: std::hash::BuildHasher + Default,
480{
481    fn py_try_from(py: Python, set: &PyFrozenSet) -> PyResult<Self> {
482        let mut map = Self::with_capacity_and_hasher(set.len(), Hasher::default());
483        for item in set {
484            let item = T::py_try_from(py, item)?;
485            map.insert(item);
486        }
487        Ok(map)
488    }
489}
490
491impl<T> PyTryFrom<Py<PyFrozenSet>> for BTreeSet<T>
492where
493    T: Ord + PyTryFrom<PyAny>,
494{
495    fn py_try_from(py: Python, set: &Py<PyFrozenSet>) -> PyResult<Self> {
496        Self::py_try_from(py, set.as_ref(py))
497    }
498}
499
500impl<T> PyTryFrom<PyFrozenSet> for BTreeSet<T>
501where
502    T: Ord + PyTryFrom<PyAny>,
503{
504    fn py_try_from(py: Python, set: &PyFrozenSet) -> PyResult<Self> {
505        let mut map = Self::new();
506        for item in set {
507            let item = T::py_try_from(py, item)?;
508            map.insert(item);
509        }
510        Ok(map)
511    }
512}
513
514// ==== Integer ====
515
516impl_try_from_self_python!(PyInt);
517impl_try_from_self_rust!(i8);
518impl_try_from_self_rust!(i16);
519impl_try_from_self_rust!(i32);
520impl_try_from_self_rust!(i64);
521impl_try_from_self_rust!(i128);
522impl_try_from_self_rust!(isize);
523impl_try_from_self_rust!(u8);
524impl_try_from_self_rust!(u16);
525impl_try_from_self_rust!(u32);
526impl_try_from_self_rust!(u64);
527impl_try_from_self_rust!(u128);
528impl_try_from_self_rust!(usize);
529impl_try_from_primitive!(PyInt => i8);
530impl_try_from_primitive!(PyInt => i16);
531impl_try_from_primitive!(PyInt => i32);
532impl_try_from_primitive!(PyInt => i64);
533impl_try_from_primitive!(PyInt => i128);
534impl_try_from_primitive!(PyInt => isize);
535impl_try_from_primitive!(PyInt => u8);
536impl_try_from_primitive!(PyInt => u16);
537impl_try_from_primitive!(PyInt => u32);
538impl_try_from_primitive!(PyInt => u64);
539impl_try_from_primitive!(PyInt => u128);
540impl_try_from_primitive!(PyInt => usize);
541
542// ==== List ====
543
544impl_try_from_self_python!(PyList);
545
546impl<P, T> PyTryFrom<Vec<P>> for Vec<T>
547where
548    T: PyTryFrom<P>,
549{
550    fn py_try_from(py: Python, item: &Vec<P>) -> PyResult<Self> {
551        item.iter().map(|item| T::py_try_from(py, item)).collect()
552    }
553}
554
555impl<T> PyTryFrom<Py<PyList>> for Vec<T>
556where
557    T: PyTryFrom<PyAny>,
558{
559    fn py_try_from(py: Python, py_list: &Py<PyList>) -> PyResult<Self> {
560        Self::py_try_from(py, py_list.as_ref(py))
561    }
562}
563
564impl<T> PyTryFrom<PyList> for Vec<T>
565where
566    T: PyTryFrom<PyAny>,
567{
568    fn py_try_from(py: Python, py_list: &PyList) -> PyResult<Self> {
569        let mut list = Self::with_capacity(py_list.len());
570
571        for item in py_list {
572            let item = T::py_try_from(py, item)?;
573            list.push(item);
574        }
575
576        Ok(list)
577    }
578}
579
580impl<T> PyTryFrom<PyAny> for Vec<T>
581where
582    T: PyTryFrom<PyAny>,
583{
584    fn py_try_from(py: Python, item: &PyAny) -> PyResult<Self> {
585        let actual: &PyList = item.downcast()?;
586        Self::py_try_from(py, actual)
587    }
588}
589
590impl<T> PyTryFrom<Py<PyList>> for Box<[T]>
591where
592    T: PyTryFrom<PyAny>,
593{
594    fn py_try_from(py: Python, py_list: &Py<PyList>) -> PyResult<Self> {
595        Self::py_try_from(py, py_list.as_ref(py))
596    }
597}
598
599impl<T> PyTryFrom<PyList> for Box<[T]>
600where
601    T: PyTryFrom<PyAny>,
602{
603    fn py_try_from(py: Python, py_list: &PyList) -> PyResult<Self> {
604        Vec::py_try_from(py, py_list).map(From::from)
605    }
606}
607
608impl<T> PyTryFrom<PyAny> for Box<[T]>
609where
610    T: PyTryFrom<PyAny>,
611{
612    fn py_try_from(py: Python, item: &PyAny) -> PyResult<Self> {
613        let actual: &PyList = item.downcast()?;
614        Self::py_try_from(py, actual)
615    }
616}
617
618// ==== Optional[T] ====
619
620impl<T, P> PyTryFrom<Option<P>> for Option<T>
621where
622    T: PyTryFrom<P>,
623{
624    fn py_try_from(py: Python, item: &Option<P>) -> PyResult<Self> {
625        item.as_ref()
626            .map_or_else(|| Ok(None), |item| T::py_try_from(py, item).map(Some))
627    }
628}
629
630// ==== Set ====
631
632impl_try_from_self_python!(PySet);
633
634impl<T, P, Hasher> PyTryFrom<HashSet<P, Hasher>> for HashSet<T, Hasher>
635where
636    T: Eq + std::hash::Hash + PyTryFrom<P>,
637    Hasher: std::hash::BuildHasher + Default,
638{
639    fn py_try_from(py: Python, set: &HashSet<P, Hasher>) -> PyResult<Self> {
640        set.iter().map(|item| T::py_try_from(py, item)).collect()
641    }
642}
643
644impl<T, Hasher> PyTryFrom<Py<PySet>> for HashSet<T, Hasher>
645where
646    T: Eq + std::hash::Hash + PyTryFrom<PyAny>,
647    Hasher: std::hash::BuildHasher + Default,
648{
649    fn py_try_from(py: Python, set: &Py<PySet>) -> PyResult<Self> {
650        Self::py_try_from(py, set.as_ref(py))
651    }
652}
653
654impl<T, Hasher> PyTryFrom<PySet> for HashSet<T, Hasher>
655where
656    T: Eq + std::hash::Hash + PyTryFrom<PyAny>,
657    Hasher: std::hash::BuildHasher + Default,
658{
659    fn py_try_from(py: Python, set: &PySet) -> PyResult<Self> {
660        let mut map = Self::with_capacity_and_hasher(set.len(), Hasher::default());
661        for item in set {
662            let item = T::py_try_from(py, item)?;
663            map.insert(item);
664        }
665        Ok(map)
666    }
667}
668
669impl<T, Hasher> PyTryFrom<PyAny> for HashSet<T, Hasher>
670where
671    T: Eq + std::hash::Hash + PyTryFrom<PyAny>,
672    Hasher: std::hash::BuildHasher + Default,
673{
674    fn py_try_from(py: Python, item: &PyAny) -> PyResult<Self> {
675        let set: &PySet = item.downcast()?;
676        Self::py_try_from(py, set)
677    }
678}
679
680impl<T, P> PyTryFrom<BTreeSet<P>> for BTreeSet<T>
681where
682    T: Ord + PyTryFrom<P>,
683{
684    fn py_try_from(py: Python, set: &BTreeSet<P>) -> PyResult<Self> {
685        set.iter().map(|item| T::py_try_from(py, item)).collect()
686    }
687}
688
689impl<T> PyTryFrom<Py<PySet>> for BTreeSet<T>
690where
691    T: Ord + PyTryFrom<PyAny>,
692{
693    fn py_try_from(py: Python, set: &Py<PySet>) -> PyResult<Self> {
694        Self::py_try_from(py, set.as_ref(py))
695    }
696}
697
698impl<T> PyTryFrom<PySet> for BTreeSet<T>
699where
700    T: Ord + PyTryFrom<PyAny>,
701{
702    fn py_try_from(py: Python, set: &PySet) -> PyResult<Self> {
703        let mut map = Self::new();
704        for item in set {
705            let item = T::py_try_from(py, item)?;
706            map.insert(item);
707        }
708        Ok(map)
709    }
710}
711
712impl<T> PyTryFrom<PyAny> for BTreeSet<T>
713where
714    T: Ord + PyTryFrom<PyAny>,
715{
716    fn py_try_from(py: Python, set: &PyAny) -> PyResult<Self> {
717        let set: &PySet = set.downcast()?;
718        <Self as PyTryFrom<PySet>>::py_try_from(py, set)
719    }
720}
721
722// ==== String ====
723
724impl_try_from_self_python!(PyString);
725impl_try_from_self_rust!(String);
726impl_try_from_py_native!(PyString => String);
727
728private_impl_py_try_from_with_pyany!(&item, _py, PyString => String {
729    item.to_str().map(ToString::to_string)
730});
731
732// ==== Time ====
733
734#[cfg(feature = "time")]
735impl_try_from_self_python!(PyTime);
736
737#[cfg(feature = "time")]
738impl_try_from_self_rust!((Time, Option<UtcOffset>));
739#[cfg(feature = "time")]
740impl_try_from_py_native!(PyTime => (Time, Option<UtcOffset>));
741
742#[cfg(feature = "time")]
743private_impl_py_try_from_with_pyany!(&item, py, PyTime => (Time, Option<UtcOffset>) {
744    let hour: u8 = item.getattr("hour")?.downcast::<PyInt>()?.extract()?;
745    let minute: u8 = item.getattr("minute")?.downcast::<PyInt>()?.extract()?;
746    let seconds: u8 = item.getattr("second")?.downcast::<PyInt>()?.extract()?;
747    let microseconds: u32 = item.getattr("microsecond")?.downcast::<PyInt>()?.extract()?;
748    let tzinfo: Option<&PyTzInfo> = item.getattr("tzinfo")?.extract()?;
749    let offset = tzinfo.map(|tzinfo| UtcOffset::py_try_from(py, tzinfo)).transpose()?;
750    let timestamp = Time::from_hms_micro(hour, minute, seconds, microseconds).map_err(|err| {
751        PyValueError::new_err(format!("Could not create a Rust Time from {hour}:{minute}:{seconds}.{microseconds}: {err}"))
752    })?;
753    Ok((timestamp, offset))
754});
755
756// ==== TzInfo ====
757
758#[cfg(feature = "time")]
759impl_try_from_self_python!(PyTzInfo);
760
761#[cfg(feature = "time")]
762impl_try_from_self_rust!(UtcOffset);
763#[cfg(feature = "time")]
764impl_try_from_py_native!(PyTzInfo => UtcOffset);
765
766#[cfg(feature = "time")]
767private_impl_py_try_from_with_pyany!(&item, py, PyTzInfo => UtcOffset {
768    let args: Py<PyAny> = (py.None(),).to_object(py);
769    let args: &PyTuple = args.extract(py)?;
770    let duration = item.call_method1("utcoffset", args).map(|any| Duration::py_try_from(py, any))??;
771    let seconds = duration.whole_seconds();
772    let seconds = seconds.try_into().map_err(|_| {
773        PyValueError::new_err(format!("Cannot create a Rust UtcOffset from {seconds} seconds -- too many seconds!"))
774    })?;
775    let offset = Self::from_whole_seconds(seconds).map_err(|_| {
776        PyValueError::new_err(format!("Cannot create a Rust UtcOffset from {seconds} seconds -- too many seconds!"))
777    })?;
778    Ok(offset)
779});
780
781// ============ End Implementations ==============