Skip to content

Remove unsound custom element example. #255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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