pyo3/conversions/std/
slice.rs

1use std::borrow::Cow;
2
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::{
6    conversion::IntoPyObject,
7    types::{PyByteArray, PyByteArrayMethods, PyBytes},
8    Bound, Py, PyAny, PyErr, PyObject, PyResult, Python,
9};
10#[allow(deprecated)]
11use crate::{IntoPy, ToPyObject};
12
13#[allow(deprecated)]
14impl IntoPy<PyObject> for &[u8] {
15    fn into_py(self, py: Python<'_>) -> PyObject {
16        PyBytes::new(py, self).unbind().into()
17    }
18}
19
20impl<'a, 'py, T> IntoPyObject<'py> for &'a [T]
21where
22    &'a T: IntoPyObject<'py>,
23    T: 'a, // MSRV
24{
25    type Target = PyAny;
26    type Output = Bound<'py, Self::Target>;
27    type Error = PyErr;
28
29    /// Turns [`&[u8]`](std::slice) into [`PyBytes`], all other `T`s will be turned into a [`PyList`]
30    ///
31    /// [`PyBytes`]: crate::types::PyBytes
32    /// [`PyList`]: crate::types::PyList
33    #[inline]
34    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
35        <&T>::borrowed_sequence_into_pyobject(self, py, crate::conversion::private::Token)
36    }
37
38    #[cfg(feature = "experimental-inspect")]
39    fn type_output() -> TypeInfo {
40        TypeInfo::union_of(&[
41            TypeInfo::builtin("bytes"),
42            TypeInfo::list_of(<&T>::type_output()),
43        ])
44    }
45}
46
47impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a [u8] {
48    fn from_py_object_bound(obj: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
49        Ok(obj.downcast::<PyBytes>()?.as_bytes())
50    }
51
52    #[cfg(feature = "experimental-inspect")]
53    fn type_input() -> TypeInfo {
54        TypeInfo::builtin("bytes")
55    }
56}
57
58/// Special-purpose trait impl to efficiently handle both `bytes` and `bytearray`
59///
60/// If the source object is a `bytes` object, the `Cow` will be borrowed and
61/// pointing into the source object, and no copying or heap allocations will happen.
62/// If it is a `bytearray`, its contents will be copied to an owned `Cow`.
63impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, [u8]> {
64    fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
65        if let Ok(bytes) = ob.downcast::<PyBytes>() {
66            return Ok(Cow::Borrowed(bytes.as_bytes()));
67        }
68
69        let byte_array = ob.downcast::<PyByteArray>()?;
70        Ok(Cow::Owned(byte_array.to_vec()))
71    }
72
73    #[cfg(feature = "experimental-inspect")]
74    fn type_input() -> TypeInfo {
75        Self::type_output()
76    }
77}
78
79#[allow(deprecated)]
80impl ToPyObject for Cow<'_, [u8]> {
81    fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
82        PyBytes::new(py, self.as_ref()).into()
83    }
84}
85
86#[allow(deprecated)]
87impl IntoPy<Py<PyAny>> for Cow<'_, [u8]> {
88    fn into_py(self, py: Python<'_>) -> Py<PyAny> {
89        self.into_pyobject(py).unwrap().into_any().unbind()
90    }
91}
92
93impl<'py, T> IntoPyObject<'py> for Cow<'_, [T]>
94where
95    T: Clone,
96    for<'a> &'a T: IntoPyObject<'py>,
97{
98    type Target = PyAny;
99    type Output = Bound<'py, Self::Target>;
100    type Error = PyErr;
101
102    /// Turns `Cow<[u8]>` into [`PyBytes`], all other `T`s will be turned into a [`PyList`]
103    ///
104    /// [`PyBytes`]: crate::types::PyBytes
105    /// [`PyList`]: crate::types::PyList
106    #[inline]
107    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
108        <&T>::borrowed_sequence_into_pyobject(self.as_ref(), py, crate::conversion::private::Token)
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use std::borrow::Cow;
115
116    use crate::{
117        conversion::IntoPyObject,
118        ffi,
119        types::{any::PyAnyMethods, PyBytes, PyBytesMethods, PyList},
120        Python,
121    };
122
123    #[test]
124    fn test_extract_bytes() {
125        Python::with_gil(|py| {
126            let py_bytes = py.eval(ffi::c_str!("b'Hello Python'"), None, None).unwrap();
127            let bytes: &[u8] = py_bytes.extract().unwrap();
128            assert_eq!(bytes, b"Hello Python");
129        });
130    }
131
132    #[test]
133    fn test_cow_impl() {
134        Python::with_gil(|py| {
135            let bytes = py.eval(ffi::c_str!(r#"b"foobar""#), None, None).unwrap();
136            let cow = bytes.extract::<Cow<'_, [u8]>>().unwrap();
137            assert_eq!(cow, Cow::<[u8]>::Borrowed(b"foobar"));
138
139            let byte_array = py
140                .eval(ffi::c_str!(r#"bytearray(b"foobar")"#), None, None)
141                .unwrap();
142            let cow = byte_array.extract::<Cow<'_, [u8]>>().unwrap();
143            assert_eq!(cow, Cow::<[u8]>::Owned(b"foobar".to_vec()));
144
145            let something_else_entirely = py.eval(ffi::c_str!("42"), None, None).unwrap();
146            something_else_entirely
147                .extract::<Cow<'_, [u8]>>()
148                .unwrap_err();
149
150            let cow = Cow::<[u8]>::Borrowed(b"foobar").into_pyobject(py).unwrap();
151            assert!(cow.is_instance_of::<PyBytes>());
152
153            let cow = Cow::<[u8]>::Owned(b"foobar".to_vec())
154                .into_pyobject(py)
155                .unwrap();
156            assert!(cow.is_instance_of::<PyBytes>());
157        });
158    }
159
160    #[test]
161    fn test_slice_intopyobject_impl() {
162        Python::with_gil(|py| {
163            let bytes: &[u8] = b"foobar";
164            let obj = bytes.into_pyobject(py).unwrap();
165            assert!(obj.is_instance_of::<PyBytes>());
166            let obj = obj.downcast_into::<PyBytes>().unwrap();
167            assert_eq!(obj.as_bytes(), bytes);
168
169            let nums: &[u16] = &[0, 1, 2, 3];
170            let obj = nums.into_pyobject(py).unwrap();
171            assert!(obj.is_instance_of::<PyList>());
172        });
173    }
174
175    #[test]
176    fn test_cow_intopyobject_impl() {
177        Python::with_gil(|py| {
178            let borrowed_bytes = Cow::<[u8]>::Borrowed(b"foobar");
179            let obj = borrowed_bytes.clone().into_pyobject(py).unwrap();
180            assert!(obj.is_instance_of::<PyBytes>());
181            let obj = obj.downcast_into::<PyBytes>().unwrap();
182            assert_eq!(obj.as_bytes(), &*borrowed_bytes);
183
184            let owned_bytes = Cow::<[u8]>::Owned(b"foobar".to_vec());
185            let obj = owned_bytes.clone().into_pyobject(py).unwrap();
186            assert!(obj.is_instance_of::<PyBytes>());
187            let obj = obj.downcast_into::<PyBytes>().unwrap();
188            assert_eq!(obj.as_bytes(), &*owned_bytes);
189
190            let borrowed_nums = Cow::<[u16]>::Borrowed(&[0, 1, 2, 3]);
191            let obj = borrowed_nums.into_pyobject(py).unwrap();
192            assert!(obj.is_instance_of::<PyList>());
193
194            let owned_nums = Cow::<[u16]>::Owned(vec![0, 1, 2, 3]);
195            let obj = owned_nums.into_pyobject(py).unwrap();
196            assert!(obj.is_instance_of::<PyList>());
197        });
198    }
199}