1#![cfg(feature = "smallvec")]
2
3#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"smallvec\"] }")]
14use 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 #[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, {
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 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}