|
1 | 1 | use crate::{
|
2 |
| - ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python, |
| 2 | + ffi, AsPyPointer, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, PyTryFrom, Python, |
3 | 3 | ToPyObject,
|
4 | 4 | };
|
5 | 5 | use std::ops::Index;
|
@@ -28,37 +28,39 @@ impl PyBytes {
|
28 | 28 |
|
29 | 29 | /// Creates a new Python `bytes` object with an `init` closure to write its contents.
|
30 | 30 | /// Before calling `init` the bytes' contents are zero-initialised.
|
31 |
| - /// |
32 |
| - /// Panics if out of memory. |
| 31 | + /// * If Python raises a MemoryError on the allocation, `new_with` will return |
| 32 | + /// it inside `Err`. |
| 33 | + /// * If `init` returns `Err(e)`, `new_with` will return `Err(e)`. |
| 34 | + /// * If `init` returns `Ok(())`, `new_with` will return `Ok(&PyBytes)`. |
33 | 35 | ///
|
34 | 36 | /// # Example
|
35 | 37 | /// ```
|
36 | 38 | /// use pyo3::{prelude::*, types::PyBytes};
|
37 | 39 | /// Python::with_gil(|py| -> PyResult<()> {
|
38 | 40 | /// let py_bytes = PyBytes::new_with(py, 10, |bytes: &mut [u8]| {
|
39 | 41 | /// bytes.copy_from_slice(b"Hello Rust");
|
40 |
| - /// }); |
| 42 | + /// Ok(()) |
| 43 | + /// })?; |
41 | 44 | /// let bytes: &[u8] = FromPyObject::extract(py_bytes)?;
|
42 | 45 | /// assert_eq!(bytes, b"Hello Rust");
|
43 | 46 | /// Ok(())
|
44 | 47 | /// });
|
45 | 48 | /// ```
|
46 |
| - pub fn new_with<F>(py: Python, len: usize, init: F) -> &PyBytes |
| 49 | + pub fn new_with<F>(py: Python, len: usize, init: F) -> PyResult<&PyBytes> |
47 | 50 | where
|
48 |
| - F: FnOnce(&mut [u8]), |
| 51 | + F: FnOnce(&mut [u8]) -> PyResult<()>, |
49 | 52 | {
|
50 | 53 | unsafe {
|
51 |
| - let length = len as ffi::Py_ssize_t; |
52 |
| - let pyptr = ffi::PyBytes_FromStringAndSize(std::ptr::null(), length); |
53 |
| - // Iff pyptr is null, py.from_owned_ptr(pyptr) will panic |
54 |
| - let pybytes = py.from_owned_ptr(pyptr); |
| 54 | + let pyptr = ffi::PyBytes_FromStringAndSize(std::ptr::null(), len as ffi::Py_ssize_t); |
| 55 | + // Check for an allocation error and return it |
| 56 | + let pypybytes: Py<PyBytes> = Py::from_owned_ptr_or_err(py, pyptr)?; |
55 | 57 | let buffer = ffi::PyBytes_AsString(pyptr) as *mut u8;
|
56 | 58 | debug_assert!(!buffer.is_null());
|
57 | 59 | // Zero-initialise the uninitialised bytestring
|
58 | 60 | std::ptr::write_bytes(buffer, 0u8, len);
|
59 | 61 | // (Further) Initialise the bytestring in init
|
60 |
| - init(std::slice::from_raw_parts_mut(buffer, len)); |
61 |
| - pybytes |
| 62 | + // If init returns an Err, pypybytearray will automatically deallocate the buffer |
| 63 | + init(std::slice::from_raw_parts_mut(buffer, len)).map(|_| pypybytes.into_ref(py)) |
62 | 64 | }
|
63 | 65 | }
|
64 | 66 |
|
@@ -129,22 +131,40 @@ mod test {
|
129 | 131 | }
|
130 | 132 |
|
131 | 133 | #[test]
|
132 |
| - fn test_bytes_new_with() { |
| 134 | + fn test_bytes_new_with() -> super::PyResult<()> { |
133 | 135 | let gil = Python::acquire_gil();
|
134 | 136 | let py = gil.python();
|
135 | 137 | let py_bytes = PyBytes::new_with(py, 10, |b: &mut [u8]| {
|
136 | 138 | b.copy_from_slice(b"Hello Rust");
|
137 |
| - }); |
138 |
| - let bytes: &[u8] = FromPyObject::extract(py_bytes).unwrap(); |
| 139 | + Ok(()) |
| 140 | + })?; |
| 141 | + let bytes: &[u8] = FromPyObject::extract(py_bytes)?; |
139 | 142 | assert_eq!(bytes, b"Hello Rust");
|
| 143 | + Ok(()) |
140 | 144 | }
|
141 | 145 |
|
142 | 146 | #[test]
|
143 |
| - fn test_bytes_new_with_zero_initialised() { |
| 147 | + fn test_bytes_new_with_zero_initialised() -> super::PyResult<()> { |
144 | 148 | let gil = Python::acquire_gil();
|
145 | 149 | let py = gil.python();
|
146 |
| - let py_bytes = PyBytes::new_with(py, 10, |_b: &mut [u8]| ()); |
147 |
| - let bytes: &[u8] = FromPyObject::extract(py_bytes).unwrap(); |
| 150 | + let py_bytes = PyBytes::new_with(py, 10, |_b: &mut [u8]| Ok(()))?; |
| 151 | + let bytes: &[u8] = FromPyObject::extract(py_bytes)?; |
148 | 152 | assert_eq!(bytes, &[0; 10]);
|
| 153 | + Ok(()) |
| 154 | + } |
| 155 | + |
| 156 | + #[test] |
| 157 | + fn test_bytes_new_with_error() { |
| 158 | + use crate::exceptions::PyValueError; |
| 159 | + let gil = Python::acquire_gil(); |
| 160 | + let py = gil.python(); |
| 161 | + let py_bytes_result = PyBytes::new_with(py, 10, |_b: &mut [u8]| { |
| 162 | + Err(PyValueError::py_err("Hello Crustaceans!")) |
| 163 | + }); |
| 164 | + assert!(py_bytes_result.is_err()); |
| 165 | + assert!(py_bytes_result |
| 166 | + .err() |
| 167 | + .unwrap() |
| 168 | + .is_instance::<PyValueError>(py)); |
149 | 169 | }
|
150 | 170 | }
|
0 commit comments