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}