pyo3/types/
bytearray.rs

1use crate::err::{PyErr, PyResult};
2use crate::ffi_ptr_ext::FfiPtrExt;
3use crate::instance::{Borrowed, Bound};
4use crate::py_result_ext::PyResultExt;
5use crate::types::any::PyAnyMethods;
6use crate::{ffi, PyAny, Python};
7#[cfg(feature = "gil-refs")]
8use crate::{AsPyPointer, PyNativeType};
9use std::slice;
10
11/// Represents a Python `bytearray`.
12///
13/// Values of this type are accessed via PyO3's smart pointers, e.g. as
14/// [`Py<PyByteArray>`][crate::Py] or [`Bound<'py, PyByteArray>`][Bound].
15///
16/// For APIs available on `bytearray` objects, see the [`PyByteArrayMethods`] trait which is implemented for
17/// [`Bound<'py, PyByteArray>`][Bound].
18#[repr(transparent)]
19pub struct PyByteArray(PyAny);
20
21pyobject_native_type_core!(PyByteArray, pyobject_native_static_type_object!(ffi::PyByteArray_Type), #checkfunction=ffi::PyByteArray_Check);
22
23impl PyByteArray {
24    /// Creates a new Python bytearray object.
25    ///
26    /// The byte string is initialized by copying the data from the `&[u8]`.
27    pub fn new_bound<'py>(py: Python<'py>, src: &[u8]) -> Bound<'py, PyByteArray> {
28        let ptr = src.as_ptr().cast();
29        let len = src.len() as ffi::Py_ssize_t;
30        unsafe {
31            ffi::PyByteArray_FromStringAndSize(ptr, len)
32                .assume_owned(py)
33                .downcast_into_unchecked()
34        }
35    }
36
37    /// Creates a new Python `bytearray` object with an `init` closure to write its contents.
38    /// Before calling `init` the bytearray is zero-initialised.
39    /// * If Python raises a MemoryError on the allocation, `new_with` will return
40    ///   it inside `Err`.
41    /// * If `init` returns `Err(e)`, `new_with` will return `Err(e)`.
42    /// * If `init` returns `Ok(())`, `new_with` will return `Ok(&PyByteArray)`.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// use pyo3::{prelude::*, types::PyByteArray};
48    ///
49    /// # fn main() -> PyResult<()> {
50    /// Python::with_gil(|py| -> PyResult<()> {
51    ///     let py_bytearray = PyByteArray::new_bound_with(py, 10, |bytes: &mut [u8]| {
52    ///         bytes.copy_from_slice(b"Hello Rust");
53    ///         Ok(())
54    ///     })?;
55    ///     let bytearray: &[u8] = unsafe { py_bytearray.as_bytes() };
56    ///     assert_eq!(bytearray, b"Hello Rust");
57    ///     Ok(())
58    /// })
59    /// # }
60    /// ```
61    pub fn new_bound_with<F>(
62        py: Python<'_>,
63        len: usize,
64        init: F,
65    ) -> PyResult<Bound<'_, PyByteArray>>
66    where
67        F: FnOnce(&mut [u8]) -> PyResult<()>,
68    {
69        unsafe {
70            // Allocate buffer and check for an error
71            let pybytearray: Bound<'_, Self> =
72                ffi::PyByteArray_FromStringAndSize(std::ptr::null(), len as ffi::Py_ssize_t)
73                    .assume_owned_or_err(py)?
74                    .downcast_into_unchecked();
75
76            let buffer: *mut u8 = ffi::PyByteArray_AsString(pybytearray.as_ptr()).cast();
77            debug_assert!(!buffer.is_null());
78            // Zero-initialise the uninitialised bytearray
79            std::ptr::write_bytes(buffer, 0u8, len);
80            // (Further) Initialise the bytearray in init
81            // If init returns an Err, pypybytearray will automatically deallocate the buffer
82            init(std::slice::from_raw_parts_mut(buffer, len)).map(|_| pybytearray)
83        }
84    }
85
86    /// Creates a new Python `bytearray` object from another Python object that
87    /// implements the buffer protocol.
88    pub fn from_bound<'py>(src: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyByteArray>> {
89        unsafe {
90            ffi::PyByteArray_FromObject(src.as_ptr())
91                .assume_owned_or_err(src.py())
92                .downcast_into_unchecked()
93        }
94    }
95}
96
97#[cfg(feature = "gil-refs")]
98impl PyByteArray {
99    /// Deprecated form of [`PyByteArray::new_bound`]
100    #[deprecated(
101        since = "0.21.0",
102        note = "`PyByteArray::new` will be replaced by `PyByteArray::new_bound` in a future PyO3 version"
103    )]
104    pub fn new<'py>(py: Python<'py>, src: &[u8]) -> &'py PyByteArray {
105        Self::new_bound(py, src).into_gil_ref()
106    }
107
108    /// Deprecated form of [`PyByteArray::new_bound_with`]
109    #[deprecated(
110        since = "0.21.0",
111        note = "`PyByteArray::new_with` will be replaced by `PyByteArray::new_bound_with` in a future PyO3 version"
112    )]
113    pub fn new_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<&PyByteArray>
114    where
115        F: FnOnce(&mut [u8]) -> PyResult<()>,
116    {
117        Self::new_bound_with(py, len, init).map(Bound::into_gil_ref)
118    }
119
120    /// Deprecated form of [`PyByteArray::from_bound`]
121    #[deprecated(
122        since = "0.21.0",
123        note = "`PyByteArray::from` will be replaced by `PyByteArray::from_bound` in a future PyO3 version"
124    )]
125    pub fn from(src: &PyAny) -> PyResult<&PyByteArray> {
126        PyByteArray::from_bound(&src.as_borrowed()).map(Bound::into_gil_ref)
127    }
128
129    /// Gets the length of the bytearray.
130    #[inline]
131    pub fn len(&self) -> usize {
132        self.as_borrowed().len()
133    }
134
135    /// Checks if the bytearray is empty.
136    pub fn is_empty(&self) -> bool {
137        self.as_borrowed().is_empty()
138    }
139
140    /// Gets the start of the buffer containing the contents of the bytearray.
141    ///
142    /// # Safety
143    ///
144    /// See the safety requirements of [`PyByteArray::as_bytes`] and [`PyByteArray::as_bytes_mut`].
145    pub fn data(&self) -> *mut u8 {
146        self.as_borrowed().data()
147    }
148
149    /// Extracts a slice of the `ByteArray`'s entire buffer.
150    ///
151    /// # Safety
152    ///
153    /// Mutation of the `bytearray` invalidates the slice. If it is used afterwards, the behavior is
154    /// undefined.
155    ///
156    /// These mutations may occur in Python code as well as from Rust:
157    /// - Calling methods like [`PyByteArray::as_bytes_mut`] and [`PyByteArray::resize`] will
158    ///   invalidate the slice.
159    /// - Actions like dropping objects or raising exceptions can invoke `__del__`methods or signal
160    ///   handlers, which may execute arbitrary Python code. This means that if Python code has a
161    ///   reference to the `bytearray` you cannot safely use the vast majority of PyO3's API whilst
162    ///   using the slice.
163    ///
164    /// As a result, this slice should only be used for short-lived operations without executing any
165    /// Python code, such as copying into a Vec.
166    ///
167    /// # Examples
168    ///
169    /// ```rust
170    /// use pyo3::prelude::*;
171    /// use pyo3::exceptions::PyRuntimeError;
172    /// use pyo3::types::PyByteArray;
173    ///
174    /// #[pyfunction]
175    /// fn a_valid_function(bytes: &Bound<'_, PyByteArray>) -> PyResult<()> {
176    ///     let section = {
177    ///         // SAFETY: We promise to not let the interpreter regain control
178    ///         // or invoke any PyO3 APIs while using the slice.
179    ///         let slice = unsafe { bytes.as_bytes() };
180    ///
181    ///         // Copy only a section of `bytes` while avoiding
182    ///         // `to_vec` which copies the entire thing.
183    ///         let section = slice
184    ///             .get(6..11)
185    ///             .ok_or_else(|| PyRuntimeError::new_err("input is not long enough"))?;
186    ///         Vec::from(section)
187    ///     };
188    ///
189    ///     // Now we can do things with `section` and call PyO3 APIs again.
190    ///     // ...
191    ///     # assert_eq!(&section, b"world");
192    ///
193    ///     Ok(())
194    /// }
195    /// # fn main() -> PyResult<()> {
196    /// #     Python::with_gil(|py| -> PyResult<()> {
197    /// #         let fun = wrap_pyfunction_bound!(a_valid_function, py)?;
198    /// #         let locals = pyo3::types::PyDict::new_bound(py);
199    /// #         locals.set_item("a_valid_function", fun)?;
200    /// #
201    /// #         py.run_bound(
202    /// # r#"b = bytearray(b"hello world")
203    /// # a_valid_function(b)
204    /// #
205    /// # try:
206    /// #     a_valid_function(bytearray())
207    /// # except RuntimeError as e:
208    /// #     assert str(e) == 'input is not long enough'"#,
209    /// #             None,
210    /// #             Some(&locals),
211    /// #         )?;
212    /// #
213    /// #         Ok(())
214    /// #     })
215    /// # }
216    /// ```
217    ///
218    /// # Incorrect usage
219    ///
220    /// The following `bug` function is unsound ⚠️
221    ///
222    /// ```rust,no_run
223    /// # use pyo3::prelude::*;
224    /// # use pyo3::types::PyByteArray;
225    ///
226    /// # #[allow(dead_code)]
227    /// #[pyfunction]
228    /// fn bug(py: Python<'_>, bytes: &Bound<'_, PyByteArray>) {
229    ///     let slice = unsafe { bytes.as_bytes() };
230    ///
231    ///     // This explicitly yields control back to the Python interpreter...
232    ///     // ...but it's not always this obvious. Many things do this implicitly.
233    ///     py.allow_threads(|| {
234    ///         // Python code could be mutating through its handle to `bytes`,
235    ///         // which makes reading it a data race, which is undefined behavior.
236    ///         println!("{:?}", slice[0]);
237    ///     });
238    ///
239    ///     // Python code might have mutated it, so we can not rely on the slice
240    ///     // remaining valid. As such this is also undefined behavior.
241    ///     println!("{:?}", slice[0]);
242    /// }
243    /// ```
244    pub unsafe fn as_bytes(&self) -> &[u8] {
245        self.as_borrowed().as_bytes()
246    }
247
248    /// Extracts a mutable slice of the `ByteArray`'s entire buffer.
249    ///
250    /// # Safety
251    ///
252    /// Any other accesses of the `bytearray`'s buffer invalidate the slice. If it is used
253    /// afterwards, the behavior is undefined. The safety requirements of [`PyByteArray::as_bytes`]
254    /// apply to this function as well.
255    #[allow(clippy::mut_from_ref)]
256    pub unsafe fn as_bytes_mut(&self) -> &mut [u8] {
257        self.as_borrowed().as_bytes_mut()
258    }
259
260    /// Copies the contents of the bytearray to a Rust vector.
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// # use pyo3::prelude::*;
266    /// # use pyo3::types::PyByteArray;
267    /// # Python::with_gil(|py| {
268    /// let bytearray = PyByteArray::new_bound(py, b"Hello World.");
269    /// let mut copied_message = bytearray.to_vec();
270    /// assert_eq!(b"Hello World.", copied_message.as_slice());
271    ///
272    /// copied_message[11] = b'!';
273    /// assert_eq!(b"Hello World!", copied_message.as_slice());
274    ///
275    /// pyo3::py_run!(py, bytearray, "assert bytearray == b'Hello World.'");
276    /// # });
277    /// ```
278    pub fn to_vec(&self) -> Vec<u8> {
279        self.as_borrowed().to_vec()
280    }
281
282    /// Resizes the bytearray object to the new length `len`.
283    ///
284    /// Note that this will invalidate any pointers obtained by [PyByteArray::data], as well as
285    /// any (unsafe) slices obtained from [PyByteArray::as_bytes] and [PyByteArray::as_bytes_mut].
286    pub fn resize(&self, len: usize) -> PyResult<()> {
287        self.as_borrowed().resize(len)
288    }
289}
290
291/// Implementation of functionality for [`PyByteArray`].
292///
293/// These methods are defined for the `Bound<'py, PyByteArray>` smart pointer, so to use method call
294/// syntax these methods are separated into a trait, because stable Rust does not yet support
295/// `arbitrary_self_types`.
296#[doc(alias = "PyByteArray")]
297pub trait PyByteArrayMethods<'py>: crate::sealed::Sealed {
298    /// Gets the length of the bytearray.
299    fn len(&self) -> usize;
300
301    /// Checks if the bytearray is empty.
302    fn is_empty(&self) -> bool;
303
304    /// Gets the start of the buffer containing the contents of the bytearray.
305    ///
306    /// # Safety
307    ///
308    /// See the safety requirements of [`PyByteArrayMethods::as_bytes`] and [`PyByteArrayMethods::as_bytes_mut`].
309    fn data(&self) -> *mut u8;
310
311    /// Extracts a slice of the `ByteArray`'s entire buffer.
312    ///
313    /// # Safety
314    ///
315    /// Mutation of the `bytearray` invalidates the slice. If it is used afterwards, the behavior is
316    /// undefined.
317    ///
318    /// These mutations may occur in Python code as well as from Rust:
319    /// - Calling methods like [`PyByteArrayMethods::as_bytes_mut`] and [`PyByteArrayMethods::resize`] will
320    ///   invalidate the slice.
321    /// - Actions like dropping objects or raising exceptions can invoke `__del__`methods or signal
322    ///   handlers, which may execute arbitrary Python code. This means that if Python code has a
323    ///   reference to the `bytearray` you cannot safely use the vast majority of PyO3's API whilst
324    ///   using the slice.
325    ///
326    /// As a result, this slice should only be used for short-lived operations without executing any
327    /// Python code, such as copying into a Vec.
328    ///
329    /// # Examples
330    ///
331    /// ```rust
332    /// use pyo3::prelude::*;
333    /// use pyo3::exceptions::PyRuntimeError;
334    /// use pyo3::types::PyByteArray;
335    ///
336    /// #[pyfunction]
337    /// fn a_valid_function(bytes: &Bound<'_, PyByteArray>) -> PyResult<()> {
338    ///     let section = {
339    ///         // SAFETY: We promise to not let the interpreter regain control
340    ///         // or invoke any PyO3 APIs while using the slice.
341    ///         let slice = unsafe { bytes.as_bytes() };
342    ///
343    ///         // Copy only a section of `bytes` while avoiding
344    ///         // `to_vec` which copies the entire thing.
345    ///         let section = slice
346    ///             .get(6..11)
347    ///             .ok_or_else(|| PyRuntimeError::new_err("input is not long enough"))?;
348    ///         Vec::from(section)
349    ///     };
350    ///
351    ///     // Now we can do things with `section` and call PyO3 APIs again.
352    ///     // ...
353    ///     # assert_eq!(&section, b"world");
354    ///
355    ///     Ok(())
356    /// }
357    /// # fn main() -> PyResult<()> {
358    /// #     Python::with_gil(|py| -> PyResult<()> {
359    /// #         let fun = wrap_pyfunction_bound!(a_valid_function, py)?;
360    /// #         let locals = pyo3::types::PyDict::new_bound(py);
361    /// #         locals.set_item("a_valid_function", fun)?;
362    /// #
363    /// #         py.run_bound(
364    /// # r#"b = bytearray(b"hello world")
365    /// # a_valid_function(b)
366    /// #
367    /// # try:
368    /// #     a_valid_function(bytearray())
369    /// # except RuntimeError as e:
370    /// #     assert str(e) == 'input is not long enough'"#,
371    /// #             None,
372    /// #             Some(&locals),
373    /// #         )?;
374    /// #
375    /// #         Ok(())
376    /// #     })
377    /// # }
378    /// ```
379    ///
380    /// # Incorrect usage
381    ///
382    /// The following `bug` function is unsound ⚠️
383    ///
384    /// ```rust,no_run
385    /// # use pyo3::prelude::*;
386    /// # use pyo3::types::PyByteArray;
387    ///
388    /// # #[allow(dead_code)]
389    /// #[pyfunction]
390    /// fn bug(py: Python<'_>, bytes: &Bound<'_, PyByteArray>) {
391    ///     let slice = unsafe { bytes.as_bytes() };
392    ///
393    ///     // This explicitly yields control back to the Python interpreter...
394    ///     // ...but it's not always this obvious. Many things do this implicitly.
395    ///     py.allow_threads(|| {
396    ///         // Python code could be mutating through its handle to `bytes`,
397    ///         // which makes reading it a data race, which is undefined behavior.
398    ///         println!("{:?}", slice[0]);
399    ///     });
400    ///
401    ///     // Python code might have mutated it, so we can not rely on the slice
402    ///     // remaining valid. As such this is also undefined behavior.
403    ///     println!("{:?}", slice[0]);
404    /// }
405    /// ```
406    unsafe fn as_bytes(&self) -> &[u8];
407
408    /// Extracts a mutable slice of the `ByteArray`'s entire buffer.
409    ///
410    /// # Safety
411    ///
412    /// Any other accesses of the `bytearray`'s buffer invalidate the slice. If it is used
413    /// afterwards, the behavior is undefined. The safety requirements of [`PyByteArrayMethods::as_bytes`]
414    /// apply to this function as well.
415    #[allow(clippy::mut_from_ref)]
416    unsafe fn as_bytes_mut(&self) -> &mut [u8];
417
418    /// Copies the contents of the bytearray to a Rust vector.
419    ///
420    /// # Examples
421    ///
422    /// ```
423    /// # use pyo3::prelude::*;
424    /// # use pyo3::types::PyByteArray;
425    /// # Python::with_gil(|py| {
426    /// let bytearray = PyByteArray::new_bound(py, b"Hello World.");
427    /// let mut copied_message = bytearray.to_vec();
428    /// assert_eq!(b"Hello World.", copied_message.as_slice());
429    ///
430    /// copied_message[11] = b'!';
431    /// assert_eq!(b"Hello World!", copied_message.as_slice());
432    ///
433    /// pyo3::py_run!(py, bytearray, "assert bytearray == b'Hello World.'");
434    /// # });
435    /// ```
436    fn to_vec(&self) -> Vec<u8>;
437
438    /// Resizes the bytearray object to the new length `len`.
439    ///
440    /// Note that this will invalidate any pointers obtained by [PyByteArrayMethods::data], as well as
441    /// any (unsafe) slices obtained from [PyByteArrayMethods::as_bytes] and [PyByteArrayMethods::as_bytes_mut].
442    fn resize(&self, len: usize) -> PyResult<()>;
443}
444
445impl<'py> PyByteArrayMethods<'py> for Bound<'py, PyByteArray> {
446    #[inline]
447    fn len(&self) -> usize {
448        // non-negative Py_ssize_t should always fit into Rust usize
449        unsafe { ffi::PyByteArray_Size(self.as_ptr()) as usize }
450    }
451
452    fn is_empty(&self) -> bool {
453        self.len() == 0
454    }
455
456    fn data(&self) -> *mut u8 {
457        self.as_borrowed().data()
458    }
459
460    unsafe fn as_bytes(&self) -> &[u8] {
461        self.as_borrowed().as_bytes()
462    }
463
464    #[allow(clippy::mut_from_ref)]
465    unsafe fn as_bytes_mut(&self) -> &mut [u8] {
466        self.as_borrowed().as_bytes_mut()
467    }
468
469    fn to_vec(&self) -> Vec<u8> {
470        unsafe { self.as_bytes() }.to_vec()
471    }
472
473    fn resize(&self, len: usize) -> PyResult<()> {
474        unsafe {
475            let result = ffi::PyByteArray_Resize(self.as_ptr(), len as ffi::Py_ssize_t);
476            if result == 0 {
477                Ok(())
478            } else {
479                Err(PyErr::fetch(self.py()))
480            }
481        }
482    }
483}
484
485impl<'a> Borrowed<'a, '_, PyByteArray> {
486    fn data(&self) -> *mut u8 {
487        unsafe { ffi::PyByteArray_AsString(self.as_ptr()).cast() }
488    }
489
490    #[allow(clippy::wrong_self_convention)]
491    unsafe fn as_bytes(self) -> &'a [u8] {
492        slice::from_raw_parts(self.data(), self.len())
493    }
494
495    #[allow(clippy::wrong_self_convention)]
496    unsafe fn as_bytes_mut(self) -> &'a mut [u8] {
497        slice::from_raw_parts_mut(self.data(), self.len())
498    }
499}
500
501#[cfg(feature = "gil-refs")]
502impl<'py> TryFrom<&'py PyAny> for &'py PyByteArray {
503    type Error = crate::PyErr;
504
505    /// Creates a new Python `bytearray` object from another Python object that
506    /// implements the buffer protocol.
507    fn try_from(value: &'py PyAny) -> Result<Self, Self::Error> {
508        PyByteArray::from_bound(&value.as_borrowed()).map(Bound::into_gil_ref)
509    }
510}
511
512impl<'py> TryFrom<&Bound<'py, PyAny>> for Bound<'py, PyByteArray> {
513    type Error = crate::PyErr;
514
515    /// Creates a new Python `bytearray` object from another Python object that
516    /// implements the buffer protocol.
517    fn try_from(value: &Bound<'py, PyAny>) -> Result<Self, Self::Error> {
518        PyByteArray::from_bound(value)
519    }
520}
521
522#[cfg(test)]
523mod tests {
524    use crate::types::{PyAnyMethods, PyByteArray, PyByteArrayMethods};
525    use crate::{exceptions, Bound, PyAny, PyObject, Python};
526
527    #[test]
528    fn test_len() {
529        Python::with_gil(|py| {
530            let src = b"Hello Python";
531            let bytearray = PyByteArray::new_bound(py, src);
532            assert_eq!(src.len(), bytearray.len());
533        });
534    }
535
536    #[test]
537    fn test_as_bytes() {
538        Python::with_gil(|py| {
539            let src = b"Hello Python";
540            let bytearray = PyByteArray::new_bound(py, src);
541
542            let slice = unsafe { bytearray.as_bytes() };
543            assert_eq!(src, slice);
544            assert_eq!(bytearray.data() as *const _, slice.as_ptr());
545        });
546    }
547
548    #[test]
549    fn test_as_bytes_mut() {
550        Python::with_gil(|py| {
551            let src = b"Hello Python";
552            let bytearray = PyByteArray::new_bound(py, src);
553
554            let slice = unsafe { bytearray.as_bytes_mut() };
555            assert_eq!(src, slice);
556            assert_eq!(bytearray.data(), slice.as_mut_ptr());
557
558            slice[0..5].copy_from_slice(b"Hi...");
559
560            assert_eq!(bytearray.str().unwrap(), "bytearray(b'Hi... Python')");
561        });
562    }
563
564    #[test]
565    fn test_to_vec() {
566        Python::with_gil(|py| {
567            let src = b"Hello Python";
568            let bytearray = PyByteArray::new_bound(py, src);
569
570            let vec = bytearray.to_vec();
571            assert_eq!(src, vec.as_slice());
572        });
573    }
574
575    #[test]
576    fn test_from() {
577        Python::with_gil(|py| {
578            let src = b"Hello Python";
579            let bytearray = PyByteArray::new_bound(py, src);
580
581            let ba: PyObject = bytearray.into();
582            let bytearray = PyByteArray::from_bound(ba.bind(py)).unwrap();
583
584            assert_eq!(src, unsafe { bytearray.as_bytes() });
585        });
586    }
587
588    #[test]
589    fn test_from_err() {
590        Python::with_gil(|py| {
591            if let Err(err) = PyByteArray::from_bound(py.None().bind(py)) {
592                assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
593            } else {
594                panic!("error");
595            }
596        });
597    }
598
599    #[test]
600    fn test_try_from() {
601        Python::with_gil(|py| {
602            let src = b"Hello Python";
603            let bytearray: &Bound<'_, PyAny> = &PyByteArray::new_bound(py, src);
604            let bytearray: Bound<'_, PyByteArray> = TryInto::try_into(bytearray).unwrap();
605
606            assert_eq!(src, unsafe { bytearray.as_bytes() });
607        });
608    }
609
610    #[test]
611    fn test_resize() {
612        Python::with_gil(|py| {
613            let src = b"Hello Python";
614            let bytearray = PyByteArray::new_bound(py, src);
615
616            bytearray.resize(20).unwrap();
617            assert_eq!(20, bytearray.len());
618        });
619    }
620
621    #[test]
622    fn test_byte_array_new_with() -> super::PyResult<()> {
623        Python::with_gil(|py| -> super::PyResult<()> {
624            let py_bytearray = PyByteArray::new_bound_with(py, 10, |b: &mut [u8]| {
625                b.copy_from_slice(b"Hello Rust");
626                Ok(())
627            })?;
628            let bytearray: &[u8] = unsafe { py_bytearray.as_bytes() };
629            assert_eq!(bytearray, b"Hello Rust");
630            Ok(())
631        })
632    }
633
634    #[test]
635    fn test_byte_array_new_with_zero_initialised() -> super::PyResult<()> {
636        Python::with_gil(|py| -> super::PyResult<()> {
637            let py_bytearray = PyByteArray::new_bound_with(py, 10, |_b: &mut [u8]| Ok(()))?;
638            let bytearray: &[u8] = unsafe { py_bytearray.as_bytes() };
639            assert_eq!(bytearray, &[0; 10]);
640            Ok(())
641        })
642    }
643
644    #[test]
645    fn test_byte_array_new_with_error() {
646        use crate::exceptions::PyValueError;
647        Python::with_gil(|py| {
648            let py_bytearray_result = PyByteArray::new_bound_with(py, 10, |_b: &mut [u8]| {
649                Err(PyValueError::new_err("Hello Crustaceans!"))
650            });
651            assert!(py_bytearray_result.is_err());
652            assert!(py_bytearray_result
653                .err()
654                .unwrap()
655                .is_instance_of::<PyValueError>(py));
656        })
657    }
658}