pyo3/types/
bytes.rs

1use crate::ffi_ptr_ext::FfiPtrExt;
2use crate::instance::{Borrowed, Bound};
3use crate::types::any::PyAnyMethods;
4use crate::{ffi, Py, PyAny, PyResult, Python};
5use std::ops::Index;
6use std::slice::SliceIndex;
7use std::str;
8
9/// Represents a Python `bytes` object.
10///
11/// This type is immutable.
12///
13/// Values of this type are accessed via PyO3's smart pointers, e.g. as
14/// [`Py<PyBytes>`][crate::Py] or [`Bound<'py, PyBytes>`][Bound].
15///
16/// For APIs available on `bytes` objects, see the [`PyBytesMethods`] trait which is implemented for
17/// [`Bound<'py, PyBytes>`][Bound].
18///
19/// # Equality
20///
21/// For convenience, [`Bound<'py, PyBytes>`][Bound] implements [`PartialEq<[u8]>`][PartialEq] to allow comparing the
22/// data in the Python bytes to a Rust `[u8]` byte slice.
23///
24/// This is not always the most appropriate way to compare Python bytes, as Python bytes subclasses
25/// may have different equality semantics. In situations where subclasses overriding equality might be
26/// relevant, use [`PyAnyMethods::eq`], at cost of the additional overhead of a Python method call.
27///
28/// ```rust
29/// # use pyo3::prelude::*;
30/// use pyo3::types::PyBytes;
31///
32/// # Python::with_gil(|py| {
33/// let py_bytes = PyBytes::new(py, b"foo".as_slice());
34/// // via PartialEq<[u8]>
35/// assert_eq!(py_bytes, b"foo".as_slice());
36///
37/// // via Python equality
38/// let other = PyBytes::new(py, b"foo".as_slice());
39/// assert!(py_bytes.as_any().eq(other).unwrap());
40///
41/// // Note that `eq` will convert its argument to Python using `IntoPyObject`.
42/// // Byte collections are specialized, so that the following slice will indeed
43/// // convert into a `bytes` object and not a `list`:
44/// assert!(py_bytes.as_any().eq(b"foo".as_slice()).unwrap());
45/// # });
46/// ```
47#[repr(transparent)]
48pub struct PyBytes(PyAny);
49
50pyobject_native_type_core!(PyBytes, pyobject_native_static_type_object!(ffi::PyBytes_Type), #checkfunction=ffi::PyBytes_Check);
51
52impl PyBytes {
53    /// Creates a new Python bytestring object.
54    /// The bytestring is initialized by copying the data from the `&[u8]`.
55    ///
56    /// Panics if out of memory.
57    pub fn new<'p>(py: Python<'p>, s: &[u8]) -> Bound<'p, PyBytes> {
58        let ptr = s.as_ptr().cast();
59        let len = s.len() as ffi::Py_ssize_t;
60        unsafe {
61            ffi::PyBytes_FromStringAndSize(ptr, len)
62                .assume_owned(py)
63                .downcast_into_unchecked()
64        }
65    }
66
67    /// Deprecated name for [`PyBytes::new`].
68    #[deprecated(since = "0.23.0", note = "renamed to `PyBytes::new`")]
69    #[inline]
70    pub fn new_bound<'p>(py: Python<'p>, s: &[u8]) -> Bound<'p, PyBytes> {
71        Self::new(py, s)
72    }
73
74    /// Creates a new Python `bytes` object with an `init` closure to write its contents.
75    /// Before calling `init` the bytes' contents are zero-initialised.
76    /// * If Python raises a MemoryError on the allocation, `new_with` will return
77    ///   it inside `Err`.
78    /// * If `init` returns `Err(e)`, `new_with` will return `Err(e)`.
79    /// * If `init` returns `Ok(())`, `new_with` will return `Ok(&PyBytes)`.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// use pyo3::{prelude::*, types::PyBytes};
85    ///
86    /// # fn main() -> PyResult<()> {
87    /// Python::with_gil(|py| -> PyResult<()> {
88    ///     let py_bytes = PyBytes::new_with(py, 10, |bytes: &mut [u8]| {
89    ///         bytes.copy_from_slice(b"Hello Rust");
90    ///         Ok(())
91    ///     })?;
92    ///     let bytes: &[u8] = py_bytes.extract()?;
93    ///     assert_eq!(bytes, b"Hello Rust");
94    ///     Ok(())
95    /// })
96    /// # }
97    /// ```
98    #[inline]
99    pub fn new_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<Bound<'_, PyBytes>>
100    where
101        F: FnOnce(&mut [u8]) -> PyResult<()>,
102    {
103        unsafe {
104            let pyptr = ffi::PyBytes_FromStringAndSize(std::ptr::null(), len as ffi::Py_ssize_t);
105            // Check for an allocation error and return it
106            let pybytes = pyptr.assume_owned_or_err(py)?.downcast_into_unchecked();
107            let buffer: *mut u8 = ffi::PyBytes_AsString(pyptr).cast();
108            debug_assert!(!buffer.is_null());
109            // Zero-initialise the uninitialised bytestring
110            std::ptr::write_bytes(buffer, 0u8, len);
111            // (Further) Initialise the bytestring in init
112            // If init returns an Err, pypybytearray will automatically deallocate the buffer
113            init(std::slice::from_raw_parts_mut(buffer, len)).map(|_| pybytes)
114        }
115    }
116
117    /// Deprecated name for [`PyBytes::new_with`].
118    #[deprecated(since = "0.23.0", note = "renamed to `PyBytes::new_with`")]
119    #[inline]
120    pub fn new_bound_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<Bound<'_, PyBytes>>
121    where
122        F: FnOnce(&mut [u8]) -> PyResult<()>,
123    {
124        Self::new_with(py, len, init)
125    }
126
127    /// Creates a new Python byte string object from a raw pointer and length.
128    ///
129    /// Panics if out of memory.
130    ///
131    /// # Safety
132    ///
133    /// This function dereferences the raw pointer `ptr` as the
134    /// leading pointer of a slice of length `len`. [As with
135    /// `std::slice::from_raw_parts`, this is
136    /// unsafe](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety).
137    pub unsafe fn from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> Bound<'_, PyBytes> {
138        ffi::PyBytes_FromStringAndSize(ptr.cast(), len as isize)
139            .assume_owned(py)
140            .downcast_into_unchecked()
141    }
142
143    /// Deprecated name for [`PyBytes::from_ptr`].
144    ///
145    /// # Safety
146    ///
147    /// This function dereferences the raw pointer `ptr` as the
148    /// leading pointer of a slice of length `len`. [As with
149    /// `std::slice::from_raw_parts`, this is
150    /// unsafe](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety).
151    #[deprecated(since = "0.23.0", note = "renamed to `PyBytes::from_ptr`")]
152    #[inline]
153    pub unsafe fn bound_from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> Bound<'_, PyBytes> {
154        Self::from_ptr(py, ptr, len)
155    }
156}
157
158/// Implementation of functionality for [`PyBytes`].
159///
160/// These methods are defined for the `Bound<'py, PyBytes>` smart pointer, so to use method call
161/// syntax these methods are separated into a trait, because stable Rust does not yet support
162/// `arbitrary_self_types`.
163#[doc(alias = "PyBytes")]
164pub trait PyBytesMethods<'py>: crate::sealed::Sealed {
165    /// Gets the Python string as a byte slice.
166    fn as_bytes(&self) -> &[u8];
167}
168
169impl<'py> PyBytesMethods<'py> for Bound<'py, PyBytes> {
170    #[inline]
171    fn as_bytes(&self) -> &[u8] {
172        self.as_borrowed().as_bytes()
173    }
174}
175
176impl<'a> Borrowed<'a, '_, PyBytes> {
177    /// Gets the Python string as a byte slice.
178    #[allow(clippy::wrong_self_convention)]
179    pub(crate) fn as_bytes(self) -> &'a [u8] {
180        unsafe {
181            let buffer = ffi::PyBytes_AsString(self.as_ptr()) as *const u8;
182            let length = ffi::PyBytes_Size(self.as_ptr()) as usize;
183            debug_assert!(!buffer.is_null());
184            std::slice::from_raw_parts(buffer, length)
185        }
186    }
187}
188
189impl Py<PyBytes> {
190    /// Gets the Python bytes as a byte slice. Because Python bytes are
191    /// immutable, the result may be used for as long as the reference to
192    /// `self` is held, including when the GIL is released.
193    pub fn as_bytes<'a>(&'a self, py: Python<'_>) -> &'a [u8] {
194        self.bind_borrowed(py).as_bytes()
195    }
196}
197
198/// This is the same way [Vec] is indexed.
199impl<I: SliceIndex<[u8]>> Index<I> for Bound<'_, PyBytes> {
200    type Output = I::Output;
201
202    fn index(&self, index: I) -> &Self::Output {
203        &self.as_bytes()[index]
204    }
205}
206
207/// Compares whether the Python bytes object is equal to the [u8].
208///
209/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
210impl PartialEq<[u8]> for Bound<'_, PyBytes> {
211    #[inline]
212    fn eq(&self, other: &[u8]) -> bool {
213        self.as_borrowed() == *other
214    }
215}
216
217/// Compares whether the Python bytes object is equal to the [u8].
218///
219/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
220impl PartialEq<&'_ [u8]> for Bound<'_, PyBytes> {
221    #[inline]
222    fn eq(&self, other: &&[u8]) -> bool {
223        self.as_borrowed() == **other
224    }
225}
226
227/// Compares whether the Python bytes object is equal to the [u8].
228///
229/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
230impl PartialEq<Bound<'_, PyBytes>> for [u8] {
231    #[inline]
232    fn eq(&self, other: &Bound<'_, PyBytes>) -> bool {
233        *self == other.as_borrowed()
234    }
235}
236
237/// Compares whether the Python bytes object is equal to the [u8].
238///
239/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
240impl PartialEq<&'_ Bound<'_, PyBytes>> for [u8] {
241    #[inline]
242    fn eq(&self, other: &&Bound<'_, PyBytes>) -> bool {
243        *self == other.as_borrowed()
244    }
245}
246
247/// Compares whether the Python bytes object is equal to the [u8].
248///
249/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
250impl PartialEq<Bound<'_, PyBytes>> for &'_ [u8] {
251    #[inline]
252    fn eq(&self, other: &Bound<'_, PyBytes>) -> bool {
253        **self == other.as_borrowed()
254    }
255}
256
257/// Compares whether the Python bytes object is equal to the [u8].
258///
259/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
260impl PartialEq<[u8]> for &'_ Bound<'_, PyBytes> {
261    #[inline]
262    fn eq(&self, other: &[u8]) -> bool {
263        self.as_borrowed() == other
264    }
265}
266
267/// Compares whether the Python bytes object is equal to the [u8].
268///
269/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
270impl PartialEq<[u8]> for Borrowed<'_, '_, PyBytes> {
271    #[inline]
272    fn eq(&self, other: &[u8]) -> bool {
273        self.as_bytes() == other
274    }
275}
276
277/// Compares whether the Python bytes object is equal to the [u8].
278///
279/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
280impl PartialEq<&[u8]> for Borrowed<'_, '_, PyBytes> {
281    #[inline]
282    fn eq(&self, other: &&[u8]) -> bool {
283        *self == **other
284    }
285}
286
287/// Compares whether the Python bytes object is equal to the [u8].
288///
289/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
290impl PartialEq<Borrowed<'_, '_, PyBytes>> for [u8] {
291    #[inline]
292    fn eq(&self, other: &Borrowed<'_, '_, PyBytes>) -> bool {
293        other == self
294    }
295}
296
297/// Compares whether the Python bytes object is equal to the [u8].
298///
299/// In some cases Python equality might be more appropriate; see the note on [`PyBytes`].
300impl PartialEq<Borrowed<'_, '_, PyBytes>> for &'_ [u8] {
301    #[inline]
302    fn eq(&self, other: &Borrowed<'_, '_, PyBytes>) -> bool {
303        other == self
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310
311    #[test]
312    fn test_bytes_index() {
313        Python::with_gil(|py| {
314            let bytes = PyBytes::new(py, b"Hello World");
315            assert_eq!(bytes[1], b'e');
316        });
317    }
318
319    #[test]
320    fn test_bound_bytes_index() {
321        Python::with_gil(|py| {
322            let bytes = PyBytes::new(py, b"Hello World");
323            assert_eq!(bytes[1], b'e');
324
325            let bytes = &bytes;
326            assert_eq!(bytes[1], b'e');
327        });
328    }
329
330    #[test]
331    fn test_bytes_new_with() -> super::PyResult<()> {
332        Python::with_gil(|py| -> super::PyResult<()> {
333            let py_bytes = PyBytes::new_with(py, 10, |b: &mut [u8]| {
334                b.copy_from_slice(b"Hello Rust");
335                Ok(())
336            })?;
337            let bytes: &[u8] = py_bytes.extract()?;
338            assert_eq!(bytes, b"Hello Rust");
339            Ok(())
340        })
341    }
342
343    #[test]
344    fn test_bytes_new_with_zero_initialised() -> super::PyResult<()> {
345        Python::with_gil(|py| -> super::PyResult<()> {
346            let py_bytes = PyBytes::new_with(py, 10, |_b: &mut [u8]| Ok(()))?;
347            let bytes: &[u8] = py_bytes.extract()?;
348            assert_eq!(bytes, &[0; 10]);
349            Ok(())
350        })
351    }
352
353    #[test]
354    fn test_bytes_new_with_error() {
355        use crate::exceptions::PyValueError;
356        Python::with_gil(|py| {
357            let py_bytes_result = PyBytes::new_with(py, 10, |_b: &mut [u8]| {
358                Err(PyValueError::new_err("Hello Crustaceans!"))
359            });
360            assert!(py_bytes_result.is_err());
361            assert!(py_bytes_result
362                .err()
363                .unwrap()
364                .is_instance_of::<PyValueError>(py));
365        });
366    }
367
368    #[test]
369    fn test_comparisons() {
370        Python::with_gil(|py| {
371            let b = b"hello, world".as_slice();
372            let py_bytes = PyBytes::new(py, b);
373
374            assert_eq!(py_bytes, b"hello, world".as_slice());
375
376            assert_eq!(py_bytes, b);
377            assert_eq!(&py_bytes, b);
378            assert_eq!(b, py_bytes);
379            assert_eq!(b, &py_bytes);
380
381            assert_eq!(py_bytes, *b);
382            assert_eq!(&py_bytes, *b);
383            assert_eq!(*b, py_bytes);
384            assert_eq!(*b, &py_bytes);
385
386            let py_string = py_bytes.as_borrowed();
387
388            assert_eq!(py_string, b);
389            assert_eq!(&py_string, b);
390            assert_eq!(b, py_string);
391            assert_eq!(b, &py_string);
392
393            assert_eq!(py_string, *b);
394            assert_eq!(*b, py_string);
395        })
396    }
397}