pyo3/conversions/
smallvec.rs

1#![cfg(feature = "smallvec")]
2
3//!  Conversions to and from [smallvec](https://docs.rs/smallvec/).
4//!
5//! # Setup
6//!
7//! To use this feature, add this to your **`Cargo.toml`**:
8//!
9//! ```toml
10//! [dependencies]
11//! # change * to the latest versions
12//! smallvec = "*"
13#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"),  "\", features = [\"smallvec\"] }")]
14//! ```
15//!
16//! Note that you must use compatible versions of smallvec and PyO3.
17//! The required smallvec version may vary based on the version of PyO3.
18use crate::conversion::IntoPyObject;
19use crate::exceptions::PyTypeError;
20#[cfg(feature = "experimental-inspect")]
21use crate::inspect::types::TypeInfo;
22use crate::types::any::PyAnyMethods;
23use crate::types::list::new_from_iter;
24use crate::types::{PySequence, PyString};
25use crate::PyErr;
26use crate::{err::DowncastError, ffi, Bound, FromPyObject, PyAny, PyObject, PyResult, Python};
27#[allow(deprecated)]
28use crate::{IntoPy, ToPyObject};
29use smallvec::{Array, SmallVec};
30
31#[allow(deprecated)]
32impl<A> ToPyObject for SmallVec<A>
33where
34    A: Array,
35    A::Item: ToPyObject,
36{
37    fn to_object(&self, py: Python<'_>) -> PyObject {
38        self.as_slice().to_object(py)
39    }
40}
41
42#[allow(deprecated)]
43impl<A> IntoPy<PyObject> for SmallVec<A>
44where
45    A: Array,
46    A::Item: IntoPy<PyObject>,
47{
48    fn into_py(self, py: Python<'_>) -> PyObject {
49        let mut iter = self.into_iter().map(|e| e.into_py(py));
50        let list = new_from_iter(py, &mut iter);
51        list.into()
52    }
53}
54
55impl<'py, A> IntoPyObject<'py> for SmallVec<A>
56where
57    A: Array,
58    A::Item: IntoPyObject<'py>,
59{
60    type Target = PyAny;
61    type Output = Bound<'py, Self::Target>;
62    type Error = PyErr;
63
64    /// Turns [`SmallVec<u8>`] into [`PyBytes`], all other `T`s will be turned into a [`PyList`]
65    ///
66    /// [`PyBytes`]: crate::types::PyBytes
67    /// [`PyList`]: crate::types::PyList
68    #[inline]
69    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
70        <A::Item>::owned_sequence_into_pyobject(self, py, crate::conversion::private::Token)
71    }
72
73    #[cfg(feature = "experimental-inspect")]
74    fn type_output() -> TypeInfo {
75        TypeInfo::list_of(A::Item::type_output())
76    }
77}
78
79impl<'a, 'py, A> IntoPyObject<'py> for &'a SmallVec<A>
80where
81    A: Array,
82    &'a A::Item: IntoPyObject<'py>,
83    A::Item: 'a, // MSRV
84{
85    type Target = PyAny;
86    type Output = Bound<'py, Self::Target>;
87    type Error = PyErr;
88
89    #[inline]
90    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
91        self.as_slice().into_pyobject(py)
92    }
93
94    #[cfg(feature = "experimental-inspect")]
95    fn type_output() -> TypeInfo {
96        TypeInfo::list_of(<&A::Item>::type_output())
97    }
98}
99
100impl<'py, A> FromPyObject<'py> for SmallVec<A>
101where
102    A: Array,
103    A::Item: FromPyObject<'py>,
104{
105    fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
106        if obj.is_instance_of::<PyString>() {
107            return Err(PyTypeError::new_err("Can't extract `str` to `SmallVec`"));
108        }
109        extract_sequence(obj)
110    }
111
112    #[cfg(feature = "experimental-inspect")]
113    fn type_input() -> TypeInfo {
114        TypeInfo::sequence_of(A::Item::type_input())
115    }
116}
117
118fn extract_sequence<'py, A>(obj: &Bound<'py, PyAny>) -> PyResult<SmallVec<A>>
119where
120    A: Array,
121    A::Item: FromPyObject<'py>,
122{
123    // Types that pass `PySequence_Check` usually implement enough of the sequence protocol
124    // to support this function and if not, we will only fail extraction safely.
125    let seq = unsafe {
126        if ffi::PySequence_Check(obj.as_ptr()) != 0 {
127            obj.downcast_unchecked::<PySequence>()
128        } else {
129            return Err(DowncastError::new(obj, "Sequence").into());
130        }
131    };
132
133    let mut sv = SmallVec::with_capacity(seq.len().unwrap_or(0));
134    for item in seq.try_iter()? {
135        sv.push(item?.extract::<A::Item>()?);
136    }
137    Ok(sv)
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use crate::types::{PyBytes, PyBytesMethods, PyDict, PyList};
144
145    #[test]
146    #[allow(deprecated)]
147    fn test_smallvec_into_py() {
148        Python::with_gil(|py| {
149            let sv: SmallVec<[u64; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect();
150            let hso: PyObject = sv.clone().into_py(py);
151            let l = PyList::new(py, [1, 2, 3, 4, 5]).unwrap();
152            assert!(l.eq(hso).unwrap());
153        });
154    }
155
156    #[test]
157    fn test_smallvec_from_py_object() {
158        Python::with_gil(|py| {
159            let l = PyList::new(py, [1, 2, 3, 4, 5]).unwrap();
160            let sv: SmallVec<[u64; 8]> = l.extract().unwrap();
161            assert_eq!(sv.as_slice(), [1, 2, 3, 4, 5]);
162        });
163    }
164
165    #[test]
166    fn test_smallvec_from_py_object_fails() {
167        Python::with_gil(|py| {
168            let dict = PyDict::new(py);
169            let sv: PyResult<SmallVec<[u64; 8]>> = dict.extract();
170            assert_eq!(
171                sv.unwrap_err().to_string(),
172                "TypeError: 'dict' object cannot be converted to 'Sequence'"
173            );
174        });
175    }
176
177    #[test]
178    fn test_smallvec_into_pyobject() {
179        Python::with_gil(|py| {
180            let sv: SmallVec<[u64; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect();
181            let hso = sv.into_pyobject(py).unwrap();
182            let l = PyList::new(py, [1, 2, 3, 4, 5]).unwrap();
183            assert!(l.eq(hso).unwrap());
184        });
185    }
186
187    #[test]
188    fn test_smallvec_intopyobject_impl() {
189        Python::with_gil(|py| {
190            let bytes: SmallVec<[u8; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect();
191            let obj = bytes.clone().into_pyobject(py).unwrap();
192            assert!(obj.is_instance_of::<PyBytes>());
193            let obj = obj.downcast_into::<PyBytes>().unwrap();
194            assert_eq!(obj.as_bytes(), &*bytes);
195
196            let nums: SmallVec<[u16; 8]> = [1, 2, 3, 4, 5].iter().cloned().collect();
197            let obj = nums.into_pyobject(py).unwrap();
198            assert!(obj.is_instance_of::<PyList>());
199        });
200    }
201}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here