Skip to content

Commit

Permalink
Merge pull request #255 from PyO3/unsound-custom-element
Browse files Browse the repository at this point in the history
Remove unsound custom element example.
  • Loading branch information
adamreichold authored Jan 23, 2022
2 parents 54b14e0 + fdb366e commit 2da4787
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 44 deletions.
56 changes: 50 additions & 6 deletions src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,23 +629,25 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
}
}

/// Construct PyArray from
/// [`ndarray::Array`](https://docs.rs/ndarray/latest/ndarray/type.Array.html).
/// Constructs a `PyArray` from [`ndarray::Array`]
///
/// This method uses internal [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html)
/// of `ndarray::Array` as numpy array.
/// This method uses the internal [`Vec`] of the `ndarray::Array` as the base object of the NumPy array.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate ndarray;
/// use ndarray::array;
/// use numpy::PyArray;
///
/// pyo3::Python::with_gil(|py| {
/// let pyarray = PyArray::from_owned_array(py, array![[1, 2], [3, 4]]);
/// assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]);
/// });
/// ```
pub fn from_owned_array<'py>(py: Python<'py>, arr: Array<T, D>) -> &'py Self {
IntoPyArray::into_pyarray(arr, py)
let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
let data_ptr = arr.as_ptr();
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, arr) }
}

/// Get the immutable reference of the specified element, with checking the passed index is valid.
Expand Down Expand Up @@ -858,6 +860,48 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
}
}

impl<D: Dimension> PyArray<PyObject, D> {
/// Constructs a `PyArray` containing objects from [`ndarray::Array`]
///
/// This method uses the internal [`Vec`] of the `ndarray::Array` as the base object the NumPy array.
///
/// # Example
///
/// ```
/// use ndarray::array;
/// use pyo3::{pyclass, Py, Python};
/// use numpy::PyArray;
///
/// #[pyclass]
/// struct CustomElement {
/// foo: i32,
/// bar: f64,
/// }
///
/// Python::with_gil(|py| {
/// let array = array![
/// Py::new(py, CustomElement {
/// foo: 1,
/// bar: 2.0,
/// }).unwrap(),
/// Py::new(py, CustomElement {
/// foo: 3,
/// bar: 4.0,
/// }).unwrap(),
/// ];
///
/// let pyarray = PyArray::from_owned_object_array(py, array);
///
/// assert!(pyarray.readonly().get(0).unwrap().as_ref(py).is_instance::<CustomElement>().unwrap());
/// });
/// ```
pub fn from_owned_object_array<'py, T>(py: Python<'py>, arr: Array<Py<T>, D>) -> &'py Self {
let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
let data_ptr = arr.as_ptr() as *const PyObject;
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, arr) }
}
}

impl<T: Copy + Element> PyArray<T, Ix0> {
/// Get the element of zero-dimensional PyArray.
///
Expand Down
4 changes: 1 addition & 3 deletions src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ where
type Item = A;
type Dim = D;
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
let (strides, dims) = (self.npy_strides(), self.raw_dim());
let data_ptr = self.as_ptr();
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, self) }
PyArray::from_owned_array(py, self)
}
}

Expand Down
38 changes: 6 additions & 32 deletions src/dtype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ impl PyArrayDescr {

/// Represents that a type can be an element of `PyArray`.
///
/// Currently, only integer/float/complex types are supported.
/// Currently, only integer/float/complex/object types are supported.
/// If you come up with a nice implementation for some other types, we're happy to receive your PR :)
/// You may refer to the [numpy document](https://numpy.org/doc/stable/reference/c-api/dtype.html#enumerated-types)
/// for all types that numpy supports.
Expand All @@ -310,38 +310,12 @@ impl PyArrayDescr {
///
/// # Custom element types
///
/// You can implement this trait to manage arrays of custom element types, but they still need to be stored
/// on Python's heap using PyO3's [Py](pyo3::Py) type.
/// Note that we cannot safely store `Py<T>` where `T: PyClass`, because the type information would be
/// eliminated in the resulting NumPy array.
/// In other words, objects are always treated as `Py<PyAny>` (a.k.a. `PyObject`) by Python code,
/// and only `Py<PyAny>` can be stored in a type safe manner.
///
/// ```
/// use numpy::{ndarray::Array2, Element, PyArray, PyArrayDescr, ToPyArray};
/// use pyo3::{pyclass, Py, Python};
///
/// #[pyclass]
/// pub struct CustomElement;
///
/// // The transparent wrapper is necessary as one cannot implement
/// // a foreign trait (`Element`) on a foreign type (`Py`) directly.
/// #[derive(Clone)]
/// #[repr(transparent)]
/// pub struct Wrapper(pub Py<CustomElement>);
///
/// unsafe impl Element for Wrapper {
/// const IS_COPY: bool = false;
///
/// fn get_dtype(py: Python) -> &PyArrayDescr {
/// PyArrayDescr::object(py)
/// }
/// }
///
/// Python::with_gil(|py| {
/// let array = Array2::<Wrapper>::from_shape_fn((2, 3), |(_i, _j)| {
/// Wrapper(Py::new(py, CustomElement).unwrap())
/// });
///
/// let _array: &PyArray<Wrapper, _> = array.to_pyarray(py);
/// });
/// ```
/// You can however create `ndarray::Array<Py<T>, D>` and turn that into a NumPy array safely and efficiently using [`from_owned_object_array`][crate::PyArray::from_owned_object_array].
pub unsafe trait Element: Clone + Send {
/// Flag that indicates whether this type is trivially copyable.
///
Expand Down
4 changes: 1 addition & 3 deletions src/slice_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use pyo3::pyclass_slots::PyClassDummySlot;
use pyo3::type_object::{LazyStaticType, PyTypeInfo};
use pyo3::{ffi, types::PyAny, PyCell};

use crate::dtype::Element;

/// Utility type to safely store Box<[_]> or Vec<_> on the Python heap
pub(crate) struct PySliceContainer {
ptr: *mut u8,
Expand Down Expand Up @@ -69,7 +67,7 @@ impl<T: Send> From<Vec<T>> for PySliceContainer {

impl<A, D> From<ArrayBase<OwnedRepr<A>, D>> for PySliceContainer
where
A: Element,
A: Send,
D: Dimension,
{
fn from(data: ArrayBase<OwnedRepr<A>, D>) -> Self {
Expand Down

0 comments on commit 2da4787

Please sign in to comment.