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!(§ion, 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!(§ion, 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}