Description
I'm using PyArray::from_borrowed_array_bound
to expose an immutable data buffer from a Rust struct to Python space via Numpy. The type needs to uphold some invariants about its internal data, which means I want to prevent Numpy from allowing writes, so I'm trying to unset the WRITEABLE
flag before returning the object from Rust space.
Currently, I'm doing something like this:
use pyo3::prelude::*;
use numpy::{prelude::*, PyArray1, npyffi};
use ndarray::aview1;
#[pyclass]
struct NonwriteableData(Box<[u8]>);
#[pymethods]
impl NonwriteableData {
fn data(slf_: Bound<Self>) -> Bound<PyArray1<u8>> {
let arr = aview1(&self.0);
// This feels comfortably unsafe, and I'm upholding the requirements
// to not re-allocate, drop, etc the underlying pointer from Rust.
let out = unsafe {
PyArray1::borrow_from_array_bound(&arr, slf_.into_any())
};
// This _feels_ like there's a safe API that can be made?
unsafe {
(*out.as_array_ptr()).flags &= !npyffi::flags::NPY_ARRAY_WRITEABLE;
}
out
}
}
I'm not certain that I'm entirely correct in doing this in the first place. If I am, then my second unsafe
block feels like it could have a safe abstraction wrapping it? If I understand correctly, if we can assert/verify that there are no existing PyReadwriteArray
s taken out on the underlying memory in Rust space (which I haven't done explicitly here, since I only just created the array object), then removing the WRITEABLE
flag should be a safe operation.
Please correct me if I've made soundness errors here. If I haven't, could we potentially add methods like nonwriteable
/ try_nonwriteable
that unset this flag in a safe manner?