From 1f4f01bab83fb8d1fc196249145a41fd2a62d316 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Thu, 27 Jun 2024 10:53:25 +0200 Subject: [PATCH 01/31] Bumped pyo3 dependency to v0.22.0 --- Cargo.toml | 8 ++++++-- src/datetime.rs | 2 +- src/dtype.rs | 3 ++- src/strings.rs | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b634e8720..650dc0eac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,16 @@ num-complex = ">= 0.2, < 0.5" num-integer = "0.1" num-traits = "0.2" ndarray = ">= 0.15, < 0.17" -pyo3 = { version = "0.21.0", default-features = false, features = ["macros"] } +pyo3 = { version = "0.22.0", default-features = false, features = ["macros"] } rustc-hash = "1.1" [dev-dependencies] -pyo3 = { version = "0.21.0", default-features = false, features = ["auto-initialize"] } +pyo3 = { version = "0.22.0", default-features = false, features = ["auto-initialize"] } nalgebra = { version = "0.32", default-features = false, features = ["std"] } [package.metadata.docs.rs] all-features = true + +[features] +default = ["gil-refs"] +"gil-refs" = ["pyo3/gil-refs"] diff --git a/src/datetime.rs b/src/datetime.rs index 794170616..b5a4c0758 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -243,7 +243,7 @@ impl TypeDescriptors { } }; - dtype.clone().into_bound(py) + dtype.bind(py).to_owned() } } diff --git a/src/dtype.rs b/src/dtype.rs index bc68dbaa8..5e7b0118f 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -825,6 +825,7 @@ mod tests { use super::*; use pyo3::{py_run, types::PyTypeMethods}; + use pyo3::types::PyString; use crate::npyffi::{is_numpy_2, NPY_NEEDS_PYAPI}; @@ -851,7 +852,7 @@ mod tests { #[test] fn test_dtype_names() { - fn type_name<'py, T: Element>(py: Python<'py>) -> String { + fn type_name(py: Python) -> Bound { dtype_bound::(py).typeobj().qualname().unwrap() } Python::with_gil(|py| { diff --git a/src/strings.rs b/src/strings.rs index 1f440fbda..5e96da17f 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -191,7 +191,7 @@ impl TypeDescriptors { } }; - dtype.clone().into_bound(py) + dtype.bind(py).to_owned() } } From b6bb822347c3a91cf966752cf43d38d3d41de1ac Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Thu, 27 Jun 2024 11:18:55 +0200 Subject: [PATCH 02/31] Update convert.rs --- src/convert.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index 4e50c5126..342758d96 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -41,10 +41,7 @@ pub trait IntoPyArray: Sized { type Dim: Dimension; /// Deprecated form of [`IntoPyArray::into_pyarray_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `IntoPyArray::into_pyarray_bound` in the future" - )] + #[cfg(feature = "gil-refs")] fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray { Self::into_pyarray_bound(self, py).into_gil_ref() } @@ -152,10 +149,7 @@ pub trait ToPyArray { type Dim: Dimension; /// Deprecated form of [`ToPyArray::to_pyarray_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `ToPyArray::to_pyarray_bound` in the future" - )] + #[cfg(feature = "gil-refs")] fn to_pyarray<'py>(&self, py: Python<'py>) -> &'py PyArray { Self::to_pyarray_bound(self, py).into_gil_ref() } From 09944ecd58f0dbb018f359f0036792304c02826d Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Thu, 27 Jun 2024 11:20:08 +0200 Subject: [PATCH 03/31] Update sum_products --- src/lib.rs | 2 +- src/sum_products.rs | 25 ++++++++----------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 44dc09622..e640d56d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,7 +117,7 @@ pub use crate::dtype::{ pub use crate::error::{BorrowError, FromVecError, NotContiguousError}; pub use crate::npyffi::{PY_ARRAY_API, PY_UFUNC_API}; pub use crate::strings::{PyFixedString, PyFixedUnicode}; -#[allow(deprecated)] +#[cfg(feature = "gil-refs")] pub use crate::sum_products::{dot, einsum, inner}; pub use crate::sum_products::{dot_bound, einsum_bound, inner_bound}; pub use crate::untyped_array::{PyUntypedArray, PyUntypedArrayMethods}; diff --git a/src/sum_products.rs b/src/sum_products.rs index 32cff6203..3dd4a206e 100644 --- a/src/sum_products.rs +++ b/src/sum_products.rs @@ -4,7 +4,9 @@ use std::ptr::null_mut; use ndarray::{Dimension, IxDyn}; use pyo3::types::PyAnyMethods; -use pyo3::{Borrowed, Bound, FromPyObject, PyNativeType, PyResult}; +#[cfg(feature = "gil-refs")] +use pyo3::PyNativeType; +use pyo3::{Borrowed, Bound, FromPyObject, PyResult}; use crate::array::PyArray; use crate::dtype::Element; @@ -13,6 +15,7 @@ use crate::npyffi::{array::PY_ARRAY_API, NPY_CASTING, NPY_ORDER}; /// Return value of a function that can yield either an array or a scalar. pub trait ArrayOrScalar<'py, T>: FromPyObject<'py> {} +#[cfg(feature = "gil-refs")] impl<'py, T, D> ArrayOrScalar<'py, T> for &'py PyArray where T: Element, @@ -30,10 +33,7 @@ where impl<'py, T> ArrayOrScalar<'py, T> for T where T: Element + FromPyObject<'py> {} /// Deprecated form of [`inner_bound`] -#[deprecated( - since = "0.21.0", - note = "will be replaced by `inner_bound` in the future" -)] +#[cfg(feature = "gil-refs")] pub fn inner<'py, T, DIN1, DIN2, OUT>( array1: &'py PyArray, array2: &'py PyArray, @@ -100,10 +100,7 @@ where } /// Deprecated form of [`dot_bound`] -#[deprecated( - since = "0.21.0", - note = "will be replaced by `dot_bound` in the future" -)] +#[cfg(feature = "gil-refs")] pub fn dot<'py, T, DIN1, DIN2, OUT>( array1: &'py PyArray, array2: &'py PyArray, @@ -176,10 +173,7 @@ where } /// Deprecated form of [`einsum_bound`] -#[deprecated( - since = "0.21.0", - note = "will be replaced by `einsum_bound` in the future" -)] +#[cfg(feature = "gil-refs")] pub fn einsum<'py, T, OUT>(subscripts: &str, arrays: &[&'py PyArray]) -> PyResult where T: Element, @@ -226,10 +220,7 @@ where } /// Deprecated form of [`einsum_bound!`][crate::einsum_bound!] -#[deprecated( - since = "0.21.0", - note = "will be replaced by `einsum_bound!` in the future" -)] +#[cfg(feature = "gil-refs")] #[macro_export] macro_rules! einsum { ($subscripts:literal $(,$array:ident)+ $(,)*) => {{ From e932abd7634123dbd4ce1558b935995a60a65aed Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Thu, 27 Jun 2024 11:23:19 +0200 Subject: [PATCH 04/31] Update untyped_arrays --- src/untyped_array.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/untyped_array.rs b/src/untyped_array.rs index c51a89458..d70dfb5c4 100644 --- a/src/untyped_array.rs +++ b/src/untyped_array.rs @@ -3,9 +3,11 @@ //! [ndarray]: https://numpy.org/doc/stable/reference/arrays.ndarray.html use std::slice; +#[cfg(feature = "gil-refs")] +use pyo3::PyNativeType; use pyo3::{ ffi, pyobject_native_type_extract, pyobject_native_type_named, types::PyAnyMethods, - AsPyPointer, Bound, IntoPy, PyAny, PyNativeType, PyObject, PyTypeInfo, Python, + AsPyPointer, Bound, IntoPy, PyAny, PyObject, PyTypeInfo, Python, }; use crate::array::{PyArray, PyArrayMethods}; @@ -85,6 +87,7 @@ impl IntoPy for PyUntypedArray { pyobject_native_type_extract!(PyUntypedArray); +#[cfg(feature = "gil-refs")] impl PyUntypedArray { /// Returns a raw pointer to the underlying [`PyArrayObject`][npyffi::PyArrayObject]. #[inline] From d6295796f13af35498ff2552ef56af6bc6e59612 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Thu, 27 Jun 2024 16:21:44 +0200 Subject: [PATCH 05/31] update dtype --- src/dtype.rs | 98 +++++++++++++++++++++++----------------------------- src/lib.rs | 2 +- 2 files changed, 44 insertions(+), 56 deletions(-) diff --git a/src/dtype.rs b/src/dtype.rs index 5e7b0118f..a7f6eaa64 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -10,11 +10,12 @@ use pyo3::{ ffi::{self, PyTuple_Size}, pyobject_native_type_extract, pyobject_native_type_named, types::{PyAnyMethods, PyDict, PyDictMethods, PyTuple, PyType}, - AsPyPointer, Borrowed, Bound, PyAny, PyNativeType, PyObject, PyResult, PyTypeInfo, Python, - ToPyObject, + Borrowed, Bound, PyAny, PyObject, PyResult, PyTypeInfo, Python, ToPyObject, }; #[cfg(feature = "half")] use pyo3::{sync::GILOnceCell, Py}; +#[cfg(feature = "gil-refs")] +use pyo3::{AsPyPointer, PyNativeType}; use crate::npyffi::{ NpyTypes, PyArray_Descr, PyDataType_ALIGNMENT, PyDataType_ELSIZE, PyDataType_FIELDS, @@ -60,6 +61,7 @@ unsafe impl PyTypeInfo for PyArrayDescr { unsafe { PY_ARRAY_API.get_type_object(py, NpyTypes::PyArrayDescr_Type) } } + #[cfg(feature = "gil-refs")] fn is_type_of(ob: &PyAny) -> bool { unsafe { ffi::PyObject_TypeCheck(ob.as_ptr(), Self::type_object_raw(ob.py())) > 0 } } @@ -68,10 +70,7 @@ unsafe impl PyTypeInfo for PyArrayDescr { pyobject_native_type_extract!(PyArrayDescr); /// Returns the type descriptor ("dtype") for a registered type. -#[deprecated( - since = "0.21.0", - note = "This will be replaced by `dtype_bound` in the future." -)] +#[cfg(feature = "gil-refs")] pub fn dtype<'py, T: Element>(py: Python<'py>) -> &'py PyArrayDescr { T::get_dtype_bound(py).into_gil_ref() } @@ -82,18 +81,6 @@ pub fn dtype_bound<'py, T: Element>(py: Python<'py>) -> Bound<'py, PyArrayDescr> } impl PyArrayDescr { - /// Creates a new type descriptor ("dtype") object from an arbitrary object. - /// - /// Equivalent to invoking the constructor of [`numpy.dtype`][dtype]. - /// - /// [dtype]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.html - #[deprecated( - since = "0.21.0", - note = "This will be replace by `new_bound` in the future." - )] - pub fn new<'py, T: ToPyObject + ?Sized>(py: Python<'py>, ob: &T) -> PyResult<&'py Self> { - Self::new_bound(py, ob).map(Bound::into_gil_ref) - } /// Creates a new type descriptor ("dtype") object from an arbitrary object. /// /// Equivalent to invoking the constructor of [`numpy.dtype`][dtype]. @@ -117,6 +104,42 @@ impl PyArrayDescr { inner(py, ob.to_object(py)) } + /// Shortcut for creating a type descriptor of `object` type. + pub fn object_bound(py: Python<'_>) -> Bound<'_, Self> { + Self::from_npy_type(py, NPY_TYPES::NPY_OBJECT) + } + + /// Returns the type descriptor for a registered type. + pub fn of_bound<'py, T: Element>(py: Python<'py>) -> Bound<'py, Self> { + T::get_dtype_bound(py) + } + + fn from_npy_type<'py>(py: Python<'py>, npy_type: NPY_TYPES) -> Bound<'py, Self> { + unsafe { + let descr = PY_ARRAY_API.PyArray_DescrFromType(py, npy_type as _); + Bound::from_owned_ptr(py, descr.cast()).downcast_into_unchecked() + } + } + + pub(crate) fn new_from_npy_type<'py>(py: Python<'py>, npy_type: NPY_TYPES) -> Bound<'py, Self> { + unsafe { + let descr = PY_ARRAY_API.PyArray_DescrNewFromType(py, npy_type as _); + Bound::from_owned_ptr(py, descr.cast()).downcast_into_unchecked() + } + } +} + +#[cfg(feature = "gil-refs")] +impl PyArrayDescr { + /// Creates a new type descriptor ("dtype") object from an arbitrary object. + /// + /// Equivalent to invoking the constructor of [`numpy.dtype`][dtype]. + /// + /// [dtype]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.html + pub fn new<'py, T: ToPyObject + ?Sized>(py: Python<'py>, ob: &T) -> PyResult<&'py Self> { + Self::new_bound(py, ob).map(Bound::into_gil_ref) + } + /// Returns `self` as `*mut PyArray_Descr`. pub fn as_dtype_ptr(&self) -> *mut PyArray_Descr { self.as_borrowed().as_dtype_ptr() @@ -130,52 +153,20 @@ impl PyArrayDescr { } /// Shortcut for creating a type descriptor of `object` type. - #[deprecated( - since = "0.21.0", - note = "This will be replaced by `object_bound` in the future." - )] pub fn object<'py>(py: Python<'py>) -> &'py Self { Self::object_bound(py).into_gil_ref() } - /// Shortcut for creating a type descriptor of `object` type. - pub fn object_bound(py: Python<'_>) -> Bound<'_, Self> { - Self::from_npy_type(py, NPY_TYPES::NPY_OBJECT) - } - /// Returns the type descriptor for a registered type. - #[deprecated( - since = "0.21.0", - note = "This will be replaced by `of_bound` in the future." - )] pub fn of<'py, T: Element>(py: Python<'py>) -> &'py Self { Self::of_bound::(py).into_gil_ref() } - /// Returns the type descriptor for a registered type. - pub fn of_bound<'py, T: Element>(py: Python<'py>) -> Bound<'py, Self> { - T::get_dtype_bound(py) - } - /// Returns true if two type descriptors are equivalent. pub fn is_equiv_to(&self, other: &Self) -> bool { self.as_borrowed().is_equiv_to(&other.as_borrowed()) } - fn from_npy_type<'py>(py: Python<'py>, npy_type: NPY_TYPES) -> Bound<'py, Self> { - unsafe { - let descr = PY_ARRAY_API.PyArray_DescrFromType(py, npy_type as _); - Bound::from_owned_ptr(py, descr.cast()).downcast_into_unchecked() - } - } - - pub(crate) fn new_from_npy_type<'py>(py: Python<'py>, npy_type: NPY_TYPES) -> Bound<'py, Self> { - unsafe { - let descr = PY_ARRAY_API.PyArray_DescrNewFromType(py, npy_type as _); - Bound::from_owned_ptr(py, descr.cast()).downcast_into_unchecked() - } - } - /// Returns the [array scalar][arrays-scalars] corresponding to this type descriptor. /// /// Equivalent to [`numpy.dtype.type`][dtype-type]. @@ -701,10 +692,7 @@ pub unsafe trait Element: Clone + Send { const IS_COPY: bool; /// Returns the associated type descriptor ("dtype") for the given element type. - #[deprecated( - since = "0.21.0", - note = "This will be replaced by `get_dtype_bound` in the future." - )] + #[cfg(feature = "gil-refs")] fn get_dtype<'py>(py: Python<'py>) -> &'py PyArrayDescr { Self::get_dtype_bound(py).into_gil_ref() } @@ -824,8 +812,8 @@ unsafe impl Element for PyObject { mod tests { use super::*; - use pyo3::{py_run, types::PyTypeMethods}; use pyo3::types::PyString; + use pyo3::{py_run, types::PyTypeMethods}; use crate::npyffi::{is_numpy_2, NPY_NEEDS_PYAPI}; diff --git a/src/lib.rs b/src/lib.rs index e640d56d3..c43995c4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,7 @@ pub use crate::borrow::{ PyReadwriteArray5, PyReadwriteArray6, PyReadwriteArrayDyn, }; pub use crate::convert::{IntoPyArray, NpyIndex, ToNpyDims, ToPyArray}; -#[allow(deprecated)] +#[cfg(feature = "gil-refs")] pub use crate::dtype::dtype; pub use crate::dtype::{ dtype_bound, Complex32, Complex64, Element, PyArrayDescr, PyArrayDescrMethods, From 592ad11521a70d43c414552c7c43f60c9cb7702a Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Thu, 27 Jun 2024 16:21:27 +0200 Subject: [PATCH 06/31] Update array --- src/array.rs | 302 ++++++++++++++++++++++--------------------------- tests/array.rs | 6 + 2 files changed, 141 insertions(+), 167 deletions(-) diff --git a/src/array.rs b/src/array.rs index 76d62d1c3..73585198b 100644 --- a/src/array.rs +++ b/src/array.rs @@ -19,9 +19,11 @@ use num_traits::AsPrimitive; use pyo3::{ ffi, pyobject_native_type_base, types::{DerefToPyAny, PyAnyMethods, PyModule}, - AsPyPointer, Bound, DowncastError, FromPyObject, IntoPy, Py, PyAny, PyErr, PyNativeType, - PyObject, PyResult, PyTypeInfo, Python, + AsPyPointer, Bound, DowncastError, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, PyTypeInfo, + Python, }; +#[cfg(feature = "gil-refs")] +use pyo3::{FromPyObject, PyNativeType}; use crate::borrow::{PyReadonlyArray, PyReadwriteArray}; use crate::cold; @@ -170,6 +172,7 @@ impl IntoPy>> for &'_ PyArray { } } +#[cfg(feature = "gil-refs")] impl From<&'_ PyArray> for Py> { #[inline] fn from(other: &PyArray) -> Self { @@ -189,6 +192,7 @@ impl IntoPy for PyArray { } } +#[cfg(feature = "gil-refs")] impl<'py, T: Element, D: Dimension> FromPyObject<'py> for &'py PyArray { fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { PyArray::extract(ob).cloned().map(Bound::into_gil_ref) @@ -201,7 +205,10 @@ impl PyArray { pub fn as_untyped(&self) -> &PyUntypedArray { unsafe { &*(self as *const Self as *const PyUntypedArray) } } +} +#[cfg(feature = "gil-refs")] +impl PyArray { /// Turn `&PyArray` into `Py>`, /// i.e. a pointer into Python's heap which is independent of the GIL lifetime. /// @@ -222,7 +229,6 @@ impl PyArray { /// assert_eq!(array.bind(py).readonly().as_slice().unwrap(), [0.0; 5]); /// }); /// ``` - #[deprecated(since = "0.21.0", note = "use Bound::unbind() instead")] pub fn to_owned(&self) -> Py { unsafe { Py::from_borrowed_ptr(self.py(), self.as_ptr()) } } @@ -232,7 +238,6 @@ impl PyArray { /// # Safety /// /// This is a wrapper around [`pyo3::FromPyPointer::from_owned_ptr_or_opt`] and inherits its safety contract. - #[deprecated(since = "0.21.0", note = "use Bound::from_owned_ptr() instead")] pub unsafe fn from_owned_ptr<'py>(py: Python<'py>, ptr: *mut ffi::PyObject) -> &'py Self { #[allow(deprecated)] py.from_owned_ptr(ptr) @@ -243,7 +248,6 @@ impl PyArray { /// # Safety /// /// This is a wrapper around [`pyo3::FromPyPointer::from_borrowed_ptr_or_opt`] and inherits its safety contract. - #[deprecated(since = "0.21.0", note = "use Bound::from_borrowed_ptr() instead")] pub unsafe fn from_borrowed_ptr<'py>(py: Python<'py>, ptr: *mut ffi::PyObject) -> &'py Self { #[allow(deprecated)] py.from_borrowed_ptr(ptr) @@ -287,27 +291,6 @@ impl PyArray { Ok(array) } - /// Same as [`shape`][PyUntypedArray::shape], but returns `D` instead of `&[usize]`. - #[inline(always)] - pub fn dims(&self) -> D { - D::from_dimension(&Dim(self.shape())).expect(DIMENSIONALITY_MISMATCH_ERR) - } - - /// Deprecated form of [`PyArray::new_bound`] - /// - /// # Safety - /// Same as [`PyArray::new_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `PyArray::new_bound` in the future" - )] - pub unsafe fn new<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> &Self - where - ID: IntoDimension, - { - Self::new_bound(py, dims, is_fortran).into_gil_ref() - } - /// Creates a new uninitialized NumPy array. /// /// If `is_fortran` is true, then it has Fortran/column-major order, @@ -432,24 +415,6 @@ impl PyArray { Self::new_with_data(py, dims, strides, data_ptr, container.cast()) } - /// Deprecated form of [`PyArray::borrow_from_array_bound`] - /// - /// # Safety - /// Same as [`PyArray::borrow_from_array_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `PyArray::borrow_from_array_bound` in the future" - )] - pub unsafe fn borrow_from_array<'py, S>( - array: &ArrayBase, - container: &'py PyAny, - ) -> &'py Self - where - S: Data, - { - Self::borrow_from_array_bound(array, (*container.as_borrowed()).clone()).into_gil_ref() - } - /// Creates a NumPy array backed by `array` and ties its ownership to the Python object `container`. /// /// # Safety @@ -501,18 +466,6 @@ impl PyArray { ) } - /// Deprecated form of [`PyArray::zeros_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `PyArray::zeros_bound` in the future" - )] - pub fn zeros<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> &Self - where - ID: IntoDimension, - { - Self::zeros_bound(py, dims, is_fortran).into_gil_ref() - } - /// Construct a new NumPy array filled with zeros. /// /// If `is_fortran` is true, then it has Fortran/column-major order, @@ -555,6 +508,104 @@ impl PyArray { } } + /// Constructs a NumPy from an [`ndarray::Array`] + /// + /// This method uses the internal [`Vec`] of the [`ndarray::Array`] as the base object of the NumPy array. + /// + /// # Example + /// + /// ``` + /// use numpy::{PyArray, PyArrayMethods}; + /// use ndarray::array; + /// use pyo3::Python; + /// + /// Python::with_gil(|py| { + /// let pyarray = PyArray::from_owned_array_bound(py, array![[1, 2], [3, 4]]); + /// + /// assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]); + /// }); + /// ``` + pub fn from_owned_array_bound(py: Python<'_>, mut arr: Array) -> Bound<'_, Self> { + let (strides, dims) = (arr.npy_strides(), arr.raw_dim()); + let data_ptr = arr.as_mut_ptr(); + unsafe { + Self::from_raw_parts( + py, + dims, + strides.as_ptr(), + data_ptr, + PySliceContainer::from(arr), + ) + } + } + + /// Construct a NumPy array from a [`ndarray::ArrayBase`]. + /// + /// This method allocates memory in Python's heap via the NumPy API, + /// and then copies all elements of the array there. + /// + /// # Example + /// + /// ``` + /// use numpy::{PyArray, PyArrayMethods}; + /// use ndarray::array; + /// use pyo3::Python; + /// + /// Python::with_gil(|py| { + /// let pyarray = PyArray::from_array_bound(py, &array![[1, 2], [3, 4]]); + /// + /// assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]); + /// }); + /// ``` + pub fn from_array_bound<'py, S>(py: Python<'py>, arr: &ArrayBase) -> Bound<'py, Self> + where + S: Data, + { + ToPyArray::to_pyarray_bound(arr, py) + } +} + +#[cfg(feature = "gil-refs")] +impl PyArray { + /// Same as [`shape`][PyUntypedArray::shape], but returns `D` instead of `&[usize]`. + #[inline(always)] + pub fn dims(&self) -> D { + D::from_dimension(&Dim(self.shape())).expect(DIMENSIONALITY_MISMATCH_ERR) + } + + /// Deprecated form of [`PyArray::new_bound`] + /// + /// # Safety + /// Same as [`PyArray::new_bound`] + pub unsafe fn new<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> &Self + where + ID: IntoDimension, + { + Self::new_bound(py, dims, is_fortran).into_gil_ref() + } + + /// Deprecated form of [`PyArray::borrow_from_array_bound`] + /// + /// # Safety + /// Same as [`PyArray::borrow_from_array_bound`] + pub unsafe fn borrow_from_array<'py, S>( + array: &ArrayBase, + container: &'py PyAny, + ) -> &'py Self + where + S: Data, + { + Self::borrow_from_array_bound(array, (*container.as_borrowed()).clone()).into_gil_ref() + } + + /// Deprecated form of [`PyArray::zeros_bound`] + pub fn zeros<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> &Self + where + ID: IntoDimension, + { + Self::zeros_bound(py, dims, is_fortran).into_gil_ref() + } + /// Returns an immutable view of the internal data as a slice. /// /// # Safety @@ -590,45 +641,10 @@ impl PyArray { } /// Deprecated form of [`PyArray::from_owned_array_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by PyArray::from_owned_array_bound in the future" - )] pub fn from_owned_array<'py>(py: Python<'py>, arr: Array) -> &'py Self { Self::from_owned_array_bound(py, arr).into_gil_ref() } - /// Constructs a NumPy from an [`ndarray::Array`] - /// - /// This method uses the internal [`Vec`] of the [`ndarray::Array`] as the base object of the NumPy array. - /// - /// # Example - /// - /// ``` - /// use numpy::{PyArray, PyArrayMethods}; - /// use ndarray::array; - /// use pyo3::Python; - /// - /// Python::with_gil(|py| { - /// let pyarray = PyArray::from_owned_array_bound(py, array![[1, 2], [3, 4]]); - /// - /// assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]); - /// }); - /// ``` - pub fn from_owned_array_bound(py: Python<'_>, mut arr: Array) -> Bound<'_, Self> { - let (strides, dims) = (arr.npy_strides(), arr.raw_dim()); - let data_ptr = arr.as_mut_ptr(); - unsafe { - Self::from_raw_parts( - py, - dims, - strides.as_ptr(), - data_ptr, - PySliceContainer::from(arr), - ) - } - } - /// Get a reference of the specified element if the given index is valid. /// /// # Safety @@ -805,10 +821,6 @@ impl PyArray { } /// Deprecated form of [`PyArray::from_array_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by PyArray::from_array_bound in the future" - )] pub fn from_array<'py, S>(py: Python<'py>, arr: &ArrayBase) -> &'py Self where S: Data, @@ -816,31 +828,6 @@ impl PyArray { Self::from_array_bound(py, arr).into_gil_ref() } - /// Construct a NumPy array from a [`ndarray::ArrayBase`]. - /// - /// This method allocates memory in Python's heap via the NumPy API, - /// and then copies all elements of the array there. - /// - /// # Example - /// - /// ``` - /// use numpy::{PyArray, PyArrayMethods}; - /// use ndarray::array; - /// use pyo3::Python; - /// - /// Python::with_gil(|py| { - /// let pyarray = PyArray::from_array_bound(py, &array![[1, 2], [3, 4]]); - /// - /// assert_eq!(pyarray.readonly().as_array(), array![[1, 2], [3, 4]]); - /// }); - /// ``` - pub fn from_array_bound<'py, S>(py: Python<'py>, arr: &ArrayBase) -> Bound<'py, Self> - where - S: Data, - { - ToPyArray::to_pyarray_bound(arr, py) - } - /// Get an immutable borrow of the NumPy array pub fn try_readonly(&self) -> Result, BorrowError> { PyReadonlyArray::try_new(self.as_borrowed().to_owned()) @@ -992,10 +979,7 @@ where impl PyArray { /// Deprecated form of [`PyArray::from_owned_object_array_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by PyArray::from_owned_object_array_bound in the future" - )] + #[cfg(feature = "gil-refs")] pub fn from_owned_object_array<'py, T>(py: Python<'py>, arr: Array, D>) -> &'py Self { Self::from_owned_object_array_bound(py, arr).into_gil_ref() } @@ -1053,6 +1037,7 @@ impl PyArray { } } +#[cfg(feature = "gil-refs")] impl PyArray { /// Get the single element of a zero-dimensional array. /// @@ -1063,15 +1048,6 @@ impl PyArray { } impl PyArray { - /// Deprecated form of [`PyArray::from_slice_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `PyArray::from_slice_bound` in the future" - )] - pub fn from_slice<'py>(py: Python<'py>, slice: &[T]) -> &'py Self { - Self::from_slice_bound(py, slice).into_gil_ref() - } - /// Construct a one-dimensional array from a [mod@slice]. /// /// # Example @@ -1095,16 +1071,6 @@ impl PyArray { } } - /// Deprecated form of [`PyArray::from_vec_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `PyArray::from_vec_bound` in the future" - )] - #[inline(always)] - pub fn from_vec<'py>(py: Python<'py>, vec: Vec) -> &'py Self { - Self::from_vec_bound(py, vec).into_gil_ref() - } - /// Construct a one-dimensional array from a [`Vec`][Vec]. /// /// # Example @@ -1124,18 +1090,6 @@ impl PyArray { vec.into_pyarray_bound(py) } - /// Deprecated form of [`PyArray::from_iter_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by PyArray::from_iter_bound in the future" - )] - pub fn from_iter<'py, I>(py: Python<'py>, iter: I) -> &'py Self - where - I: IntoIterator, - { - Self::from_iter_bound(py, iter).into_gil_ref() - } - /// Construct a one-dimensional array from an [`Iterator`]. /// /// If no reliable [`size_hint`][Iterator::size_hint] is available, @@ -1161,12 +1115,31 @@ impl PyArray { } } +#[cfg(feature = "gil-refs")] +impl PyArray { + /// Deprecated form of [`PyArray::from_slice_bound`] + pub fn from_slice<'py>(py: Python<'py>, slice: &[T]) -> &'py Self { + Self::from_slice_bound(py, slice).into_gil_ref() + } + + /// Deprecated form of [`PyArray::from_vec_bound`] + #[inline(always)] + pub fn from_vec<'py>(py: Python<'py>, vec: Vec) -> &'py Self { + Self::from_vec_bound(py, vec).into_gil_ref() + } + + /// Deprecated form of [`PyArray::from_iter_bound`] + pub fn from_iter<'py, I>(py: Python<'py>, iter: I) -> &'py Self + where + I: IntoIterator, + { + Self::from_iter_bound(py, iter).into_gil_ref() + } +} + impl PyArray { /// Deprecated form of [`PyArray::from_vec2_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `PyArray::from_vec2_bound` in the future" - )] + #[cfg(feature = "gil-refs")] pub fn from_vec2<'py>(py: Python<'py>, v: &[Vec]) -> Result<&'py Self, FromVecError> { Self::from_vec2_bound(py, v).map(Bound::into_gil_ref) } @@ -1216,10 +1189,7 @@ impl PyArray { impl PyArray { /// Deprecated form of [`PyArray::from_vec3_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by `PyArray::from_vec3_bound` in the future" - )] + #[cfg(feature = "gil-refs")] pub fn from_vec3<'py>(py: Python<'py>, v: &[Vec>]) -> Result<&'py Self, FromVecError> { Self::from_vec3_bound(py, v).map(Bound::into_gil_ref) } @@ -1283,6 +1253,7 @@ impl PyArray { } } +#[cfg(feature = "gil-refs")] impl PyArray { /// Copies `self` into `other`, performing a data type conversion if necessary. /// @@ -1455,10 +1426,7 @@ impl PyArray { impl> PyArray { /// Deprecated form of [`PyArray::arange_bound`] - #[deprecated( - since = "0.21.0", - note = "will be replaced by PyArray::arange_bound in the future" - )] + #[cfg(feature = "gil-refs")] pub fn arange<'py>(py: Python<'py>, start: T, stop: T, step: T) -> &Self { Self::arange_bound(py, start, stop, step).into_gil_ref() } diff --git a/tests/array.rs b/tests/array.rs index 273173c79..f85bf8bce 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -259,6 +259,7 @@ fn from_vec3_ragged() { } #[test] +#[cfg(feature = "gil-refs")] fn extract_as_fixed() { Python::with_gil(|py| { let locals = get_np_locals(py); @@ -273,6 +274,7 @@ fn extract_as_fixed() { } #[test] +#[cfg(feature = "gil-refs")] fn extract_as_dyn() { Python::with_gil(|py| { let locals = get_np_locals(py); @@ -294,6 +296,7 @@ fn extract_as_dyn() { } #[test] +#[cfg(feature = "gil-refs")] fn extract_fail_by_check() { Python::with_gil(|py| { let locals = get_np_locals(py); @@ -311,6 +314,7 @@ fn extract_fail_by_check() { } #[test] +#[cfg(feature = "gil-refs")] fn extract_fail_by_dim() { Python::with_gil(|py| { let locals = get_np_locals(py); @@ -328,6 +332,7 @@ fn extract_fail_by_dim() { } #[test] +#[cfg(feature = "gil-refs")] fn extract_fail_by_dtype() { Python::with_gil(|py| { let locals = get_np_locals(py); @@ -474,6 +479,7 @@ fn unbind_works() { } #[test] +#[cfg(feature = "gil-refs")] fn to_owned_works() { let arr: Py> = Python::with_gil(|py| { let arr = PyArray::from_slice_bound(py, &[1_i32, 2, 3]); From 2afad9a751a7366a5d3f03a0431b3a5c1f7aa1f6 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Thu, 27 Jun 2024 16:22:31 +0200 Subject: [PATCH 07/31] Don't enable gil-refs feature by default --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 650dc0eac..b467b7ba3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,5 +33,4 @@ nalgebra = { version = "0.32", default-features = false, features = ["std"] } all-features = true [features] -default = ["gil-refs"] -"gil-refs" = ["pyo3/gil-refs"] +gil-refs = ["pyo3/gil-refs"] From 79c85aab28dc201ca119956af1b8acfcdd3e4cfc Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 14:01:36 -0500 Subject: [PATCH 08/31] Added `PyClone` trait that will replace `Clone` as supertrait of `Element`. This trait can be trivially implemented for `Clone` types, but can also be implemented for types such as `PyObject` that can only be safely cloned while the GIL is held. --- src/dtype.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/dtype.rs b/src/dtype.rs index a7f6eaa64..8f3b8eff8 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -1,6 +1,7 @@ use std::mem::size_of; use std::os::raw::{c_int, c_long, c_longlong, c_short, c_uint, c_ulong, c_ulonglong, c_ushort}; use std::ptr; +use ndarray::{Array, ArrayView, Dimension}; #[cfg(feature = "half")] use half::{bf16, f16}; @@ -644,6 +645,55 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { impl Sealed for Bound<'_, PyArrayDescr> {} + +/// Weaker form of `Clone` for types that can be cloned while the GIL is held. +/// +/// Any type that implements `Clone` can trivially implement `PyClone` by forwarding +/// to the `Clone::clone` method. However, some types (notably `PyObject`) can only +/// be safely cloned while the GIL is held, and therefore cannot implement `Clone`. +/// This trait provides a mechanism for performing a clone while the GIL is held, as +/// represented by the [`Python`] token provided as an argument to the [`py_clone`] +/// method. All API's in the `numpy` crate require the GIL to be held, so this weaker +/// alternative to `Clone` is a sufficient prerequisite for implementing the +/// [`Element`] trait. +/// +/// # Implementing `PyClone` +/// Implementing this trait is trivial for most types, and simply requires defining +/// the `py_clone` method. The `vec_from_slice` and `array_from_view` methods have +/// default implementations that simply map the `py_clone` method to each item in +/// the collection, but types may want to override these implementations if there +/// is a more efficient way to perform the conversion. In particular, `Clone` types +/// may instead defer to the `ToOwned::to_owned` and `ArrayBase::to_owned` methods +/// for increased performance. +/// +/// [`py_clone`]: Self::py_clone +pub trait PyClone: Sized { + /// Create a clone of the value while the GIL is guaranteed to be held. + fn py_clone(&self, py: Python<'_>) -> Self; + + /// Create an owned copy of the slice while the GIL is guaranteed to be held. + /// + /// Some types may provide implementations of this method that are more efficient + /// than simply mapping the `py_clone` method to each element in the slice. + #[inline] + fn vec_from_slice(py: Python<'_>, slc: &[Self]) -> Vec { + slc.iter().map(|elem| elem.py_clone(py)).collect() + } + + /// Create an owned copy of the array while the GIL is guaranteed to be held. + /// + /// Some types may provide implementations of this method that are more efficient + /// than simply mapping the `py_clone` method to each element in the view. + #[inline] + fn array_from_view(py: Python<'_>, view: ArrayView<'_, Self, D>) -> Array + where + D: Dimension + { + view.map(|elem| elem.py_clone(py)) + } +} + + /// Represents that a type can be an element of `PyArray`. /// /// Currently, only integer/float/complex/object types are supported. The [NumPy documentation][enumerated-types] From b5c39085db8e6784041839b287ee20f39abaa099 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 14:11:47 -0500 Subject: [PATCH 09/31] Added `impl_py_clone` macro that provides an efficient `PyClone` impl for `Clone` types. The `impl_element_scalar` macro has been updated to invoke this new macro, and an explicit invocation was added for the types in the `datetime` module. --- src/datetime.rs | 6 +++++- src/dtype.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/datetime.rs b/src/datetime.rs index b5a4c0758..9d184fbc3 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -66,7 +66,7 @@ use std::marker::PhantomData; use pyo3::{sync::GILProtected, Bound, Py, Python}; use rustc_hash::FxHashMap; -use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods}; +use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods, impl_py_clone}; use crate::npyffi::{ PyArray_DatetimeDTypeMetaData, PyDataType_C_METADATA, NPY_DATETIMEUNIT, NPY_TYPES, }; @@ -155,6 +155,8 @@ impl From> for i64 { } } +impl_py_clone!(Datetime; [U: Unit]); + unsafe impl Element for Datetime { const IS_COPY: bool = true; @@ -190,6 +192,8 @@ impl From> for i64 { } } +impl_py_clone!(Timedelta; [U: Unit]); + unsafe impl Element for Timedelta { const IS_COPY: bool = true; diff --git a/src/dtype.rs b/src/dtype.rs index 8f3b8eff8..eab4a5bd4 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -796,6 +796,35 @@ fn npy_int_type() -> NPY_TYPES { } } +// Implements `PyClone` for a type that implements `Clone` +macro_rules! impl_py_clone { + ($ty:ty $(; [$param:ident $(: $bound:ident)?])?) => { + impl <$($param$(: $bound)*)?> $crate::dtype::PyClone for $ty { + #[inline] + fn py_clone(&self, _py: ::pyo3::Python<'_>) -> Self { + self.clone() + } + + #[inline] + fn vec_from_slice(_py: ::pyo3::Python<'_>, slc: &[Self]) -> Vec { + slc.to_owned() + } + + #[inline] + fn array_from_view( + _py: ::pyo3::Python<'_>, + view: ::ndarray::ArrayView<'_, Self, D> + ) -> ::ndarray::Array + where + D: ::ndarray::Dimension + { + view.to_owned() + } + } + } +} +pub(crate) use impl_py_clone; + macro_rules! impl_element_scalar { (@impl: $ty:ty, $npy_type:expr $(,#[$meta:meta])*) => { $(#[$meta])* @@ -806,6 +835,7 @@ macro_rules! impl_element_scalar { PyArrayDescr::from_npy_type(py, $npy_type) } } + impl_py_clone!($ty); }; ($ty:ty => $npy_type:ident $(,#[$meta:meta])*) => { impl_element_scalar!(@impl: $ty, NPY_TYPES::$npy_type $(,#[$meta])*); @@ -842,6 +872,9 @@ unsafe impl Element for bf16 { } } +#[cfg(feature = "half")] +impl_py_clone!(bf16); + impl_element_scalar!(Complex32 => NPY_CFLOAT, #[doc = "Complex type with `f32` components which maps to `numpy.csingle` (`numpy.complex64`)."]); impl_element_scalar!(Complex64 => NPY_CDOUBLE, From 4a59849906f8b1333d1c493621ba1384e7d434f7 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 14:16:33 -0500 Subject: [PATCH 10/31] Added `PyClone` impls for the types in the `strings` module. The impls were provided explicitly instead of using the `impl_py_clone` macro since making the macro flexible enough to handle the `const` param adds more complexity than it's worth. --- src/strings.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/strings.rs b/src/strings.rs index 5e96da17f..6364ceb5b 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -17,7 +17,7 @@ use pyo3::{ }; use rustc_hash::FxHashMap; -use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods}; +use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods, PyClone}; use crate::npyffi::PyDataType_SET_ELSIZE; use crate::npyffi::NPY_TYPES; @@ -84,6 +84,29 @@ unsafe impl Element for PyFixedString { } } +impl PyClone for PyFixedString { + #[inline] + fn py_clone(&self, _py: Python<'_>) -> Self { + self.clone() + } + + #[inline] + fn vec_from_slice(_py: Python<'_>, slc: &[Self]) -> Vec { + slc.to_owned() + } + + #[inline] + fn array_from_view( + _py: Python<'_>, + view: ::ndarray::ArrayView<'_, Self, D> + ) -> ::ndarray::Array + where + D: ::ndarray::Dimension + { + view.to_owned() + } +} + /// A newtype wrapper around [`[PyUCS4; N]`][Py_UCS4] to handle [`str_` scalars][numpy-str] while satisfying coherence. /// /// Note that when creating arrays of Unicode strings without an explicit `dtype`, @@ -145,6 +168,29 @@ impl From<[Py_UCS4; N]> for PyFixedUnicode { } } +impl PyClone for PyFixedUnicode { + #[inline] + fn py_clone(&self, _py: Python<'_>) -> Self { + self.clone() + } + + #[inline] + fn vec_from_slice(_py: Python<'_>, slc: &[Self]) -> Vec { + slc.to_owned() + } + + #[inline] + fn array_from_view( + _py: Python<'_>, + view: ::ndarray::ArrayView<'_, Self, D> + ) -> ::ndarray::Array + where + D: ::ndarray::Dimension + { + view.to_owned() + } +} + unsafe impl Element for PyFixedUnicode { const IS_COPY: bool = true; From 3d09a874ed786ca11263ec6d9929c398146673c4 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 14:20:42 -0500 Subject: [PATCH 11/31] Added `PyClone` impl for `PyObject`. This is the motivating impl for the changes, since `PyObject` can only be safely cloned while the GIL is held. --- src/dtype.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dtype.rs b/src/dtype.rs index eab4a5bd4..db1c8f1c2 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -883,6 +883,13 @@ impl_element_scalar!(Complex64 => NPY_CDOUBLE, #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] impl_element_scalar!(usize, isize); +impl PyClone for PyObject { + #[inline] + fn py_clone(&self, py: Python<'_>) -> Self { + self.clone_ref(py) + } +} + unsafe impl Element for PyObject { const IS_COPY: bool = false; From 5660cda63283ec2122c6552669223c38f54fe64f Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 14:34:05 -0500 Subject: [PATCH 12/31] Replaced `Clone` with `PyClone` as a supertrait of `Element`. This is necessary in order to continue supporting object arrays after the update to PyO3 0.22.0 since `Py` no longer implements `Clone` unless the 'py-clone' feature is enabled (which would be problematic for `numpy` to enable). This is a breaking change for 2 main reasons: 1. Custom `Element` impls will now need to provide a `PyClone` impl as well, since providing a blanket impl for all `T: Clone` is not possible. 2. A type parameter `T: Element` will no longer have an implicit `Clone` bound, so code relying on it will need to update the bounds to `T: Element + Clone`. --- src/dtype.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dtype.rs b/src/dtype.rs index db1c8f1c2..704793fbc 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -731,7 +731,7 @@ pub trait PyClone: Sized { /// /// [enumerated-types]: https://numpy.org/doc/stable/reference/c-api/dtype.html#enumerated-types /// [data-models]: https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models -pub unsafe trait Element: Clone + Send { +pub unsafe trait Element: PyClone + Send { /// Flag that indicates whether this type is trivially copyable. /// /// It should be set to true for all trivially copyable types (like scalar types From aa942b0349eb2e267f33e80305478f9281879054 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 14:39:56 -0500 Subject: [PATCH 13/31] Updated the private `array::clone_elements` function to require a GIL token and call the `py_clone` method instead of `Clone`. Updated usages accordingly, which is trivial since the GIL is already held everywhere it is called. --- src/array.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/array.rs b/src/array.rs index 73585198b..a9f6e0ce8 100644 --- a/src/array.rs +++ b/src/array.rs @@ -1066,7 +1066,7 @@ impl PyArray { unsafe { let array = PyArray::new_bound(py, [slice.len()], false); let mut data_ptr = array.data(); - clone_elements(slice, &mut data_ptr); + clone_elements(py, slice, &mut data_ptr); array } } @@ -1180,7 +1180,7 @@ impl PyArray { cold(); return Err(FromVecError::new(v.len(), len2)); } - clone_elements(v, &mut data_ptr); + clone_elements(py, v, &mut data_ptr); } Ok(array) } @@ -1245,7 +1245,7 @@ impl PyArray { cold(); return Err(FromVecError::new(v.len(), len3)); } - clone_elements(v, &mut data_ptr); + clone_elements(py, v, &mut data_ptr); } } Ok(array) @@ -1466,13 +1466,13 @@ impl> PyArray { } } -unsafe fn clone_elements(elems: &[T], data_ptr: &mut *mut T) { +unsafe fn clone_elements(py: Python<'_>, elems: &[T], data_ptr: &mut *mut T) { if T::IS_COPY { ptr::copy_nonoverlapping(elems.as_ptr(), *data_ptr, elems.len()); *data_ptr = data_ptr.add(elems.len()); } else { for elem in elems { - data_ptr.write(elem.clone()); + data_ptr.write(elem.py_clone(py)); *data_ptr = data_ptr.add(1); } } From f18e06d207dbfce43ebbd61544b14f984e7af8a0 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 14:55:04 -0500 Subject: [PATCH 14/31] Updated methods in the `array` module to call `PyClone` methods instead of `Clone` methods. This change is trivial since the GIL is already held everywhere that the clones occur, with the minor technicality that the GIL is not guaranteed to be held in the definition of the `PyArrayMethods` trait, but only in the actual impl block for `Bound: PyUntypedArrayMethods<'py> { where T: Element, D: Dimension, - Idx: NpyIndex, - { - unsafe { self.get(index) }.cloned() - } + Idx: NpyIndex; /// Turn an array with fixed dimensionality into one with dynamic dimensionality. fn to_dyn(&self) -> &Bound<'py, PyArray> @@ -1720,10 +1717,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> { fn to_vec(&self) -> Result, NotContiguousError> where T: Element, - D: Dimension, - { - unsafe { self.as_slice() }.map(ToOwned::to_owned) - } + D: Dimension; /// Get an immutable borrow of the NumPy array fn try_readonly(&self) -> Result, BorrowError> @@ -1827,10 +1821,7 @@ pub trait PyArrayMethods<'py, T, D>: PyUntypedArrayMethods<'py> { fn to_owned_array(&self) -> Array where T: Element, - D: Dimension, - { - unsafe { self.as_array() }.to_owned() - } + D: Dimension; /// Copies `self` into `other`, performing a data type conversion if necessary. /// @@ -2206,10 +2197,29 @@ impl<'py, T, D> PyArrayMethods<'py, T, D> for Bound<'py, PyArray> { Some(&mut *ptr) } + fn get_owned(&self, index: Idx) -> Option + where + T: Element, + D: Dimension, + Idx: NpyIndex, + { + let element = unsafe { self.get(index) }; + element.map(|elem| elem.py_clone(self.py())) + } + fn to_dyn(&self) -> &Bound<'py, PyArray> { unsafe { self.downcast_unchecked() } } + fn to_vec(&self) -> Result, NotContiguousError> + where + T: Element, + D: Dimension, + { + let slice = unsafe { self.as_slice() }; + slice.map(|slc| T::vec_from_slice(self.py(), slc)) + } + fn try_readonly(&self) -> Result, BorrowError> where T: Element, @@ -2260,6 +2270,15 @@ impl<'py, T, D> PyArrayMethods<'py, T, D> for Bound<'py, PyArray> { as_view(self, |shape, ptr| unsafe { RawArrayViewMut::from_shape_ptr(shape, ptr) }) + } + + fn to_owned_array(&self) -> Array + where + T: Element, + D: Dimension, + { + let view = unsafe { self.as_array() }; + T::array_from_view(self.py(), view) } fn copy_to(&self, other: &Bound<'py, PyArray>) -> PyResult<()> From d52ce3f8b87fa53385f9c601bdf01a90d3e4fcfe Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 15:16:03 -0500 Subject: [PATCH 15/31] Updated `ToPyArray` impls to call `py_clone` instead of `clone`. --- src/convert.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index 342758d96..fa7b26d39 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -195,7 +195,7 @@ where let array = PyArray::::new_bound(py, dim, false); let mut data_ptr = array.data(); for item in self.iter() { - data_ptr.write(item.clone()); + data_ptr.write(item.py_clone(py)); data_ptr = data_ptr.add(1); } array @@ -228,7 +228,7 @@ where ptr::copy_nonoverlapping(self.data.ptr(), data_ptr, self.len()); } else { for item in self.iter() { - data_ptr.write(item.clone()); + data_ptr.write(item.py_clone(py)); data_ptr = data_ptr.add(1); } } From e0260bc2dcaa16200fc52cf153b94567d1a1c270 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 15:16:42 -0500 Subject: [PATCH 16/31] Updated `Element` impl for `bf16` to call `py_clone` instead of `clone`. --- src/dtype.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dtype.rs b/src/dtype.rs index 704793fbc..451886143 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -867,7 +867,7 @@ unsafe impl Element for bf16 { .get_or_init(py, || { PyArrayDescr::new_bound(py, "bfloat16").expect("A package which provides a `bfloat16` data type for NumPy is required to use the `half::bf16` element type.").unbind() }) - .clone() + .clone_ref(py) .into_bound(py) } } From ef8a54535490d94452bcf0ef0a04650c2de72d7e Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 15:21:48 -0500 Subject: [PATCH 17/31] Added necessary "gil-refs" feature gate to the `nalgebra`-specific `PyArray` impl block. --- src/array.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/array.rs b/src/array.rs index f6495dbc0..a1aa5ebfc 100644 --- a/src/array.rs +++ b/src/array.rs @@ -923,6 +923,7 @@ impl PyArray { } #[cfg(feature = "nalgebra")] +#[cfg(feature = "gil-refs")] impl PyArray where N: nalgebra::Scalar + Element, From bfe3a66031cfbc18760a1597dc54941e83848e37 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 15:32:24 -0500 Subject: [PATCH 18/31] Feature-gated the crate-level `deny(missing_debug_implementations)` attribute. This is necessary since the `pyo3::pyobject_native_type_base` macro only implements `Debug` with the "gil-refs" feature since the 0.22.0 update. The `Debug` impls are no longer relevant anyway because `&PyArray`, `&PyArrayDescr`, etc. can only be obtained through the gil-refs API's. --- src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c43995c4a..7c5ef1f5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,8 +69,11 @@ as well as the [`PyReadonlyArray::try_as_matrix`] and [`PyReadwriteArray::try_as //! //! [c-api]: https://numpy.org/doc/stable/reference/c-api //! [ndarray]: https://numpy.org/doc/stable/reference/arrays.ndarray.html - -#![deny(missing_docs, missing_debug_implementations)] +#![deny(missing_docs)] +// requiring `Debug` impls is not relevant without gil-refs since `&PyArray` and +// similar aren't constructible and the `pyo3::pyobject_native_type_base` macro +// does not provide a `Debug` impl +#![cfg_attr(feature = "gil-refs", deny(missing_debug_implementations))] #[cfg(all(target_os = "windows", target_arch = "x86"))] compile_error!("Compilation for 32-bit windows is not currently supported. See https://github.com/PyO3/rust-numpy/issues/448"); From 963a28c04e66c00824ff76517daa38c7267c95e0 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 15:33:47 -0500 Subject: [PATCH 19/31] Minor tweaks to please the lint gods. --- src/array.rs | 4 ++-- src/lib.rs | 2 +- tests/array.rs | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/array.rs b/src/array.rs index a1aa5ebfc..b959354de 100644 --- a/src/array.rs +++ b/src/array.rs @@ -239,7 +239,7 @@ impl PyArray { /// /// This is a wrapper around [`pyo3::FromPyPointer::from_owned_ptr_or_opt`] and inherits its safety contract. pub unsafe fn from_owned_ptr<'py>(py: Python<'py>, ptr: *mut ffi::PyObject) -> &'py Self { - #[allow(deprecated)] + #![allow(deprecated)] py.from_owned_ptr(ptr) } @@ -249,7 +249,7 @@ impl PyArray { /// /// This is a wrapper around [`pyo3::FromPyPointer::from_borrowed_ptr_or_opt`] and inherits its safety contract. pub unsafe fn from_borrowed_ptr<'py>(py: Python<'py>, ptr: *mut ffi::PyObject) -> &'py Self { - #[allow(deprecated)] + #![allow(deprecated)] py.from_borrowed_ptr(ptr) } diff --git a/src/lib.rs b/src/lib.rs index 7c5ef1f5d..d7dc48911 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,7 @@ as well as the [`PyReadonlyArray::try_as_matrix`] and [`PyReadwriteArray::try_as //! Matrix3::new(30, 36, 42, 66, 81, 96, 102, 126, 150) //! ); //! }); -//! ``` +#![doc = "```"] //! //! [c-api]: https://numpy.org/doc/stable/reference/c-api //! [ndarray]: https://numpy.org/doc/stable/reference/arrays.ndarray.html diff --git a/tests/array.rs b/tests/array.rs index f85bf8bce..856d4af10 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -6,13 +6,15 @@ use ndarray::{array, s, Array1, Dim}; use numpy::prelude::*; use numpy::{ dtype_bound, get_array_module, npyffi::NPY_ORDER, pyarray_bound, PyArray, PyArray1, PyArray2, - PyArrayDescr, PyArrayDyn, PyFixedString, PyFixedUnicode, + PyArrayDescr, PyFixedString, PyFixedUnicode, }; use pyo3::{ py_run, pyclass, pymethods, types::{IntoPyDict, PyAnyMethods, PyDict, PyList}, - Bound, Py, PyResult, Python, + Bound, Py, Python, }; +#[cfg(feature = "gil-refs")] +use {numpy::PyArrayDyn, pyo3::PyResult}; fn get_np_locals(py: Python<'_>) -> Bound<'_, PyDict> { [("np", get_array_module(py).unwrap())].into_py_dict_bound(py) From fcd55917f9250f1624928da5df0268501fe00631 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 17:17:54 -0500 Subject: [PATCH 20/31] Formatting and cleanup. --- src/array.rs | 2 +- src/dtype.rs | 52 +++++++++++++++++++++++++------------------------- src/lib.rs | 5 ++--- src/strings.rs | 12 ++++++------ 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/array.rs b/src/array.rs index b959354de..68b7b8e1f 100644 --- a/src/array.rs +++ b/src/array.rs @@ -2271,7 +2271,7 @@ impl<'py, T, D> PyArrayMethods<'py, T, D> for Bound<'py, PyArray> { as_view(self, |shape, ptr| unsafe { RawArrayViewMut::from_shape_ptr(shape, ptr) }) - } + } fn to_owned_array(&self) -> Array where diff --git a/src/dtype.rs b/src/dtype.rs index 451886143..5b8434dd8 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -1,7 +1,6 @@ use std::mem::size_of; use std::os::raw::{c_int, c_long, c_longlong, c_short, c_uint, c_ulong, c_ulonglong, c_ushort}; use std::ptr; -use ndarray::{Array, ArrayView, Dimension}; #[cfg(feature = "half")] use half::{bf16, f16}; @@ -645,55 +644,56 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { impl Sealed for Bound<'_, PyArrayDescr> {} - /// Weaker form of `Clone` for types that can be cloned while the GIL is held. -/// -/// Any type that implements `Clone` can trivially implement `PyClone` by forwarding -/// to the `Clone::clone` method. However, some types (notably `PyObject`) can only +/// +/// Any type that implements `Clone` can trivially implement `PyClone` by forwarding +/// to the `Clone::clone` method. However, some types (notably `PyObject`) can only /// be safely cloned while the GIL is held, and therefore cannot implement `Clone`. /// This trait provides a mechanism for performing a clone while the GIL is held, as /// represented by the [`Python`] token provided as an argument to the [`py_clone`] -/// method. All API's in the `numpy` crate require the GIL to be held, so this weaker +/// method. All API's in the `numpy` crate require the GIL to be held, so this weaker /// alternative to `Clone` is a sufficient prerequisite for implementing the /// [`Element`] trait. -/// +/// /// # Implementing `PyClone` /// Implementing this trait is trivial for most types, and simply requires defining -/// the `py_clone` method. The `vec_from_slice` and `array_from_view` methods have -/// default implementations that simply map the `py_clone` method to each item in +/// the `py_clone` method. The `vec_from_slice` and `array_from_view` methods have +/// default implementations that simply map the `py_clone` method to each item in /// the collection, but types may want to override these implementations if there -/// is a more efficient way to perform the conversion. In particular, `Clone` types -/// may instead defer to the `ToOwned::to_owned` and `ArrayBase::to_owned` methods +/// is a more efficient way to perform the conversion. In particular, `Clone` types +/// may instead defer to the `ToOwned::to_owned` and `ArrayBase::to_owned` methods /// for increased performance. -/// +/// /// [`py_clone`]: Self::py_clone pub trait PyClone: Sized { /// Create a clone of the value while the GIL is guaranteed to be held. fn py_clone(&self, py: Python<'_>) -> Self; - + /// Create an owned copy of the slice while the GIL is guaranteed to be held. - /// - /// Some types may provide implementations of this method that are more efficient + /// + /// Some types may provide implementations of this method that are more efficient /// than simply mapping the `py_clone` method to each element in the slice. #[inline] fn vec_from_slice(py: Python<'_>, slc: &[Self]) -> Vec { slc.iter().map(|elem| elem.py_clone(py)).collect() } - + /// Create an owned copy of the array while the GIL is guaranteed to be held. - /// - /// Some types may provide implementations of this method that are more efficient + /// + /// Some types may provide implementations of this method that are more efficient /// than simply mapping the `py_clone` method to each element in the view. #[inline] - fn array_from_view(py: Python<'_>, view: ArrayView<'_, Self, D>) -> Array + fn array_from_view( + py: Python<'_>, + view: ::ndarray::ArrayView<'_, Self, D>, + ) -> ::ndarray::Array where - D: Dimension + D: ::ndarray::Dimension, { view.map(|elem| elem.py_clone(py)) } } - /// Represents that a type can be an element of `PyArray`. /// /// Currently, only integer/float/complex/object types are supported. The [NumPy documentation][enumerated-types] @@ -804,17 +804,17 @@ macro_rules! impl_py_clone { fn py_clone(&self, _py: ::pyo3::Python<'_>) -> Self { self.clone() } - + #[inline] fn vec_from_slice(_py: ::pyo3::Python<'_>, slc: &[Self]) -> Vec { slc.to_owned() } - + #[inline] fn array_from_view( - _py: ::pyo3::Python<'_>, + _py: ::pyo3::Python<'_>, view: ::ndarray::ArrayView<'_, Self, D> - ) -> ::ndarray::Array + ) -> ::ndarray::Array where D: ::ndarray::Dimension { @@ -930,7 +930,7 @@ mod tests { #[test] fn test_dtype_names() { - fn type_name(py: Python) -> Bound { + fn type_name(py: Python<'_>) -> Bound<'_, PyString> { dtype_bound::(py).typeobj().qualname().unwrap() } Python::with_gil(|py| { diff --git a/src/lib.rs b/src/lib.rs index d7dc48911..b71a0b582 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,9 +70,8 @@ as well as the [`PyReadonlyArray::try_as_matrix`] and [`PyReadwriteArray::try_as //! [c-api]: https://numpy.org/doc/stable/reference/c-api //! [ndarray]: https://numpy.org/doc/stable/reference/arrays.ndarray.html #![deny(missing_docs)] -// requiring `Debug` impls is not relevant without gil-refs since `&PyArray` and -// similar aren't constructible and the `pyo3::pyobject_native_type_base` macro -// does not provide a `Debug` impl +// requiring `Debug` impls is not relevant without gil-refs since `&PyArray` +// and similar aren't constructible #![cfg_attr(feature = "gil-refs", deny(missing_debug_implementations))] #[cfg(all(target_os = "windows", target_arch = "x86"))] diff --git a/src/strings.rs b/src/strings.rs index 6364ceb5b..72c361b77 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -87,7 +87,7 @@ unsafe impl Element for PyFixedString { impl PyClone for PyFixedString { #[inline] fn py_clone(&self, _py: Python<'_>) -> Self { - self.clone() + *self } #[inline] @@ -98,10 +98,10 @@ impl PyClone for PyFixedString { #[inline] fn array_from_view( _py: Python<'_>, - view: ::ndarray::ArrayView<'_, Self, D> + view: ::ndarray::ArrayView<'_, Self, D>, ) -> ::ndarray::Array where - D: ::ndarray::Dimension + D: ::ndarray::Dimension, { view.to_owned() } @@ -171,7 +171,7 @@ impl From<[Py_UCS4; N]> for PyFixedUnicode { impl PyClone for PyFixedUnicode { #[inline] fn py_clone(&self, _py: Python<'_>) -> Self { - self.clone() + *self } #[inline] @@ -182,10 +182,10 @@ impl PyClone for PyFixedUnicode { #[inline] fn array_from_view( _py: Python<'_>, - view: ::ndarray::ArrayView<'_, Self, D> + view: ::ndarray::ArrayView<'_, Self, D>, ) -> ::ndarray::Array where - D: ::ndarray::Dimension + D: ::ndarray::Dimension, { view.to_owned() } From 038f8ca4968d228f63d9705c453612e334bc35f8 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 17:26:14 -0500 Subject: [PATCH 21/31] Updated docs to use non-deprecated APIs. --- src/borrow/mod.rs | 2 +- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/borrow/mod.rs b/src/borrow/mod.rs index de927fe9a..a1cb203e3 100644 --- a/src/borrow/mod.rs +++ b/src/borrow/mod.rs @@ -305,7 +305,7 @@ where /// } /// /// Python::with_gil(|py| { - /// let np = py.eval("__import__('numpy')", None, None).unwrap(); + /// let np = py.eval_bound("__import__('numpy')", None, None).unwrap(); /// let sum_standard_layout = wrap_pyfunction!(sum_standard_layout)(py).unwrap(); /// let sum_dynamic_strides = wrap_pyfunction!(sum_dynamic_strides)(py).unwrap(); /// diff --git a/src/lib.rs b/src/lib.rs index b71a0b582..23be38dba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,10 +39,10 @@ as well as the [`PyReadonlyArray::try_as_matrix`] and [`PyReadwriteArray::try_as #![cfg_attr(not(feature = "nalgebra"), doc = "```rust,ignore")] //! use numpy::pyo3::Python; //! use numpy::nalgebra::Matrix3; -//! use numpy::{pyarray, ToPyArray}; +//! use numpy::{pyarray_bound, ToPyArray, PyArrayMethods}; //! //! Python::with_gil(|py| { -//! let py_array = pyarray![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]]; +//! let py_array = pyarray_bound![py, [0, 1, 2], [3, 4, 5], [6, 7, 8]]; //! //! let py_array_square; //! From fc86b46bf4140e14f1abd0120fbb005f41ddfad2 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Sat, 13 Jul 2024 22:56:52 -0500 Subject: [PATCH 22/31] Added `PyClone` to the crate's root namespace. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 23be38dba..90d74b6e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ pub use crate::convert::{IntoPyArray, NpyIndex, ToNpyDims, ToPyArray}; #[cfg(feature = "gil-refs")] pub use crate::dtype::dtype; pub use crate::dtype::{ - dtype_bound, Complex32, Complex64, Element, PyArrayDescr, PyArrayDescrMethods, + dtype_bound, Complex32, Complex64, Element, PyArrayDescr, PyArrayDescrMethods, PyClone, }; pub use crate::error::{BorrowError, FromVecError, NotContiguousError}; pub use crate::npyffi::{PY_ARRAY_API, PY_UFUNC_API}; From d81f0f4d10f7e10ed8fe353d27d47168e2b2265c Mon Sep 17 00:00:00 2001 From: jrudolph Date: Thu, 1 Aug 2024 10:16:22 -0500 Subject: [PATCH 23/31] Bumped pyo3 dependency version in the example crates. --- examples/linalg/Cargo.toml | 2 +- examples/parallel/Cargo.toml | 2 +- examples/simple/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/linalg/Cargo.toml b/examples/linalg/Cargo.toml index 3d855801f..fc4a3a29c 100644 --- a/examples/linalg/Cargo.toml +++ b/examples/linalg/Cargo.toml @@ -9,7 +9,7 @@ name = "rust_linalg" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.21.0", features = ["extension-module"] } +pyo3 = { version = "0.22.0", features = ["extension-module"] } numpy = { path = "../.." } ndarray-linalg = { version = "0.14.1", features = ["openblas-system"] } diff --git a/examples/parallel/Cargo.toml b/examples/parallel/Cargo.toml index d20d8bd29..af40e2fec 100644 --- a/examples/parallel/Cargo.toml +++ b/examples/parallel/Cargo.toml @@ -9,7 +9,7 @@ name = "rust_parallel" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.21.0", features = ["extension-module", "multiple-pymethods"] } +pyo3 = { version = "0.22.0", features = ["extension-module", "multiple-pymethods"] } numpy = { path = "../.." } ndarray = { version = "0.16", features = ["rayon", "blas"] } blas-src = { version = "0.8", features = ["openblas"] } diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml index caba4c2ce..b3fd06bca 100644 --- a/examples/simple/Cargo.toml +++ b/examples/simple/Cargo.toml @@ -9,7 +9,7 @@ name = "rust_ext" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.21.0", features = ["extension-module", "abi3-py37"] } +pyo3 = { version = "0.22.0", features = ["extension-module", "abi3-py37"] } numpy = { path = "../.." } [workspace] From 799c7d8edbdba26960cbee59e90b32f9f091dab7 Mon Sep 17 00:00:00 2001 From: jrudolph Date: Thu, 1 Aug 2024 18:45:57 -0500 Subject: [PATCH 24/31] Fixed example crate attempting to clone a PyObject. --- examples/simple/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/simple/src/lib.rs b/examples/simple/src/lib.rs index a2cbbe7f8..fa1c844c7 100644 --- a/examples/simple/src/lib.rs +++ b/examples/simple/src/lib.rs @@ -16,9 +16,9 @@ use pyo3::{ #[pymodule] fn rust_ext<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> { // example using generic PyObject - fn head(x: ArrayViewD<'_, PyObject>) -> PyResult { + fn head(py: Python<'_>, x: ArrayViewD<'_, PyObject>) -> PyResult { x.get(0) - .cloned() + .map(|obj| obj.clone_ref(py)) .ok_or_else(|| PyIndexError::new_err("array index out of range")) } @@ -49,7 +49,7 @@ fn rust_ext<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> { #[pyfn(m)] #[pyo3(name = "head")] fn head_py<'py>(x: PyReadonlyArrayDyn<'py, PyObject>) -> PyResult { - head(x.as_array()) + head(x.py(), x.as_array()) } // wrapper of `axpy` From 690f06cbaf0e64473c7ef407c06642d019c9bd02 Mon Sep 17 00:00:00 2001 From: Joshua Rudolph Date: Fri, 2 Aug 2024 11:19:59 -0500 Subject: [PATCH 25/31] Bumped the MSRV and PyO3 version in README.md Note that the `numpy` version will still need to be bumped in the README (along with Cargo.toml) before released. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d90960ab7..961d955e4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ rust-numpy =========== [![Actions Status](https://github.com/PyO3/rust-numpy/workflows/CI/badge.svg)](https://github.com/PyO3/rust-numpy/actions) [![Crate](https://img.shields.io/crates/v/numpy.svg)](https://crates.io/crates/numpy) -[![Minimum rustc 1.48](https://img.shields.io/badge/rustc-1.48+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) +[![Minimum rustc 1.63](https://img.shields.io/badge/rustc-1.63+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) [![Documentation](https://docs.rs/numpy/badge.svg)](https://docs.rs/numpy) [![codecov](https://codecov.io/gh/PyO3/rust-numpy/branch/main/graph/badge.svg)](https://codecov.io/gh/PyO3/rust-numpy) @@ -13,7 +13,7 @@ Rust bindings for the NumPy C-API. - [Current main](https://pyo3.github.io/rust-numpy) ## Requirements -- Rust >= 1.48.0 +- Rust >= 1.63.0 - Basically, our MSRV follows the one of [PyO3](https://github.com/PyO3/pyo3) - Python >= 3.7 - Python 3.6 support was dropped from 0.16 @@ -38,7 +38,7 @@ name = "rust_ext" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.21", features = ["extension-module"] } +pyo3 = { version = "0.22", features = ["extension-module"] } numpy = "0.21" ``` @@ -93,7 +93,7 @@ fn rust_ext<'py>(_py: Python<'py>, m: &Bound<'py, PyModule>) -> PyResult<()> { name = "numpy-test" [dependencies] -pyo3 = { version = "0.21", features = ["auto-initialize"] } +pyo3 = { version = "0.22", features = ["auto-initialize"] } numpy = "0.21" ``` From 26266ee8e0d3697a6d7cdfefcc1c880fdd2974d1 Mon Sep 17 00:00:00 2001 From: Joshua Rudolph Date: Sat, 5 Oct 2024 14:02:32 -0500 Subject: [PATCH 26/31] Restored deprecation warning for `IntoPyArray::into_pyarray` and `ToPyArray::to_pyarray` --- src/convert.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/convert.rs b/src/convert.rs index fa7b26d39..28296255b 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -41,6 +41,10 @@ pub trait IntoPyArray: Sized { type Dim: Dimension; /// Deprecated form of [`IntoPyArray::into_pyarray_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `IntoPyArray::into_pyarray_bound` in the future" + )] #[cfg(feature = "gil-refs")] fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray { Self::into_pyarray_bound(self, py).into_gil_ref() @@ -149,6 +153,10 @@ pub trait ToPyArray { type Dim: Dimension; /// Deprecated form of [`ToPyArray::to_pyarray_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `ToPyArray::to_pyarray_bound` in the future" + )] #[cfg(feature = "gil-refs")] fn to_pyarray<'py>(&self, py: Python<'py>) -> &'py PyArray { Self::to_pyarray_bound(self, py).into_gil_ref() From 0c9f6e9b8967560893f004a1ab371c7f6af9576f Mon Sep 17 00:00:00 2001 From: Joshua Rudolph Date: Sat, 5 Oct 2024 15:02:53 -0500 Subject: [PATCH 27/31] Removed `PyClone` trait, moved it methods to `Element`, and updated implementations/usages. The updates including renaming the `py_clone` method to `clone_ref` to match that of `Py::clone_ref`, and replacing the `impl_py_clone!` macro with `clone_methods_impl!`. --- src/array.rs | 4 +- src/convert.rs | 4 +- src/datetime.rs | 10 ++-- src/dtype.rs | 148 ++++++++++++++++++++---------------------------- src/lib.rs | 2 +- src/strings.rs | 50 ++-------------- 6 files changed, 75 insertions(+), 143 deletions(-) diff --git a/src/array.rs b/src/array.rs index 68b7b8e1f..8c6a63528 100644 --- a/src/array.rs +++ b/src/array.rs @@ -1473,7 +1473,7 @@ unsafe fn clone_elements(py: Python<'_>, elems: &[T], data_ptr: &mut *data_ptr = data_ptr.add(elems.len()); } else { for elem in elems { - data_ptr.write(elem.py_clone(py)); + data_ptr.write(elem.clone_ref(py)); *data_ptr = data_ptr.add(1); } } @@ -2205,7 +2205,7 @@ impl<'py, T, D> PyArrayMethods<'py, T, D> for Bound<'py, PyArray> { Idx: NpyIndex, { let element = unsafe { self.get(index) }; - element.map(|elem| elem.py_clone(self.py())) + element.map(|elem| elem.clone_ref(self.py())) } fn to_dyn(&self) -> &Bound<'py, PyArray> { diff --git a/src/convert.rs b/src/convert.rs index 28296255b..a58d5c427 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -203,7 +203,7 @@ where let array = PyArray::::new_bound(py, dim, false); let mut data_ptr = array.data(); for item in self.iter() { - data_ptr.write(item.py_clone(py)); + data_ptr.write(item.clone_ref(py)); data_ptr = data_ptr.add(1); } array @@ -236,7 +236,7 @@ where ptr::copy_nonoverlapping(self.data.ptr(), data_ptr, self.len()); } else { for item in self.iter() { - data_ptr.write(item.py_clone(py)); + data_ptr.write(item.clone_ref(py)); data_ptr = data_ptr.add(1); } } diff --git a/src/datetime.rs b/src/datetime.rs index 9d184fbc3..8d4b7cfad 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -66,7 +66,7 @@ use std::marker::PhantomData; use pyo3::{sync::GILProtected, Bound, Py, Python}; use rustc_hash::FxHashMap; -use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods, impl_py_clone}; +use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods, clone_methods_impl}; use crate::npyffi::{ PyArray_DatetimeDTypeMetaData, PyDataType_C_METADATA, NPY_DATETIMEUNIT, NPY_TYPES, }; @@ -155,8 +155,6 @@ impl From> for i64 { } } -impl_py_clone!(Datetime; [U: Unit]); - unsafe impl Element for Datetime { const IS_COPY: bool = true; @@ -165,6 +163,8 @@ unsafe impl Element for Datetime { DTYPES.from_unit(py, U::UNIT) } + + clone_methods_impl!(Self); } impl fmt::Debug for Datetime { @@ -192,8 +192,6 @@ impl From> for i64 { } } -impl_py_clone!(Timedelta; [U: Unit]); - unsafe impl Element for Timedelta { const IS_COPY: bool = true; @@ -202,6 +200,8 @@ unsafe impl Element for Timedelta { DTYPES.from_unit(py, U::UNIT) } + + clone_methods_impl!(Self); } impl fmt::Debug for Timedelta { diff --git a/src/dtype.rs b/src/dtype.rs index 5b8434dd8..31e7cb8c7 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -10,10 +10,10 @@ use pyo3::{ ffi::{self, PyTuple_Size}, pyobject_native_type_extract, pyobject_native_type_named, types::{PyAnyMethods, PyDict, PyDictMethods, PyTuple, PyType}, - Borrowed, Bound, PyAny, PyObject, PyResult, PyTypeInfo, Python, ToPyObject, + Borrowed, Bound, Py, PyAny, PyObject, PyResult, PyTypeInfo, Python, ToPyObject, }; #[cfg(feature = "half")] -use pyo3::{sync::GILOnceCell, Py}; +use pyo3::{sync::GILOnceCell}; #[cfg(feature = "gil-refs")] use pyo3::{AsPyPointer, PyNativeType}; @@ -644,56 +644,6 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { impl Sealed for Bound<'_, PyArrayDescr> {} -/// Weaker form of `Clone` for types that can be cloned while the GIL is held. -/// -/// Any type that implements `Clone` can trivially implement `PyClone` by forwarding -/// to the `Clone::clone` method. However, some types (notably `PyObject`) can only -/// be safely cloned while the GIL is held, and therefore cannot implement `Clone`. -/// This trait provides a mechanism for performing a clone while the GIL is held, as -/// represented by the [`Python`] token provided as an argument to the [`py_clone`] -/// method. All API's in the `numpy` crate require the GIL to be held, so this weaker -/// alternative to `Clone` is a sufficient prerequisite for implementing the -/// [`Element`] trait. -/// -/// # Implementing `PyClone` -/// Implementing this trait is trivial for most types, and simply requires defining -/// the `py_clone` method. The `vec_from_slice` and `array_from_view` methods have -/// default implementations that simply map the `py_clone` method to each item in -/// the collection, but types may want to override these implementations if there -/// is a more efficient way to perform the conversion. In particular, `Clone` types -/// may instead defer to the `ToOwned::to_owned` and `ArrayBase::to_owned` methods -/// for increased performance. -/// -/// [`py_clone`]: Self::py_clone -pub trait PyClone: Sized { - /// Create a clone of the value while the GIL is guaranteed to be held. - fn py_clone(&self, py: Python<'_>) -> Self; - - /// Create an owned copy of the slice while the GIL is guaranteed to be held. - /// - /// Some types may provide implementations of this method that are more efficient - /// than simply mapping the `py_clone` method to each element in the slice. - #[inline] - fn vec_from_slice(py: Python<'_>, slc: &[Self]) -> Vec { - slc.iter().map(|elem| elem.py_clone(py)).collect() - } - - /// Create an owned copy of the array while the GIL is guaranteed to be held. - /// - /// Some types may provide implementations of this method that are more efficient - /// than simply mapping the `py_clone` method to each element in the view. - #[inline] - fn array_from_view( - py: Python<'_>, - view: ::ndarray::ArrayView<'_, Self, D>, - ) -> ::ndarray::Array - where - D: ::ndarray::Dimension, - { - view.map(|elem| elem.py_clone(py)) - } -} - /// Represents that a type can be an element of `PyArray`. /// /// Currently, only integer/float/complex/object types are supported. The [NumPy documentation][enumerated-types] @@ -731,7 +681,7 @@ pub trait PyClone: Sized { /// /// [enumerated-types]: https://numpy.org/doc/stable/reference/c-api/dtype.html#enumerated-types /// [data-models]: https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models -pub unsafe trait Element: PyClone + Send { +pub unsafe trait Element: Sized + Send { /// Flag that indicates whether this type is trivially copyable. /// /// It should be set to true for all trivially copyable types (like scalar types @@ -749,6 +699,33 @@ pub unsafe trait Element: PyClone + Send { /// Returns the associated type descriptor ("dtype") for the given element type. fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr>; + + /// Create a clone of the value while the GIL is guaranteed to be held. + fn clone_ref(&self, py: Python<'_>) -> Self; + + /// Create an owned copy of the slice while the GIL is guaranteed to be held. + /// + /// Some types may provide implementations of this method that are more efficient + /// than simply mapping the `py_clone` method to each element in the slice. + #[inline] + fn vec_from_slice(py: Python<'_>, slc: &[Self]) -> Vec { + slc.iter().map(|elem| elem.clone_ref(py)).collect() + } + + /// Create an owned copy of the array while the GIL is guaranteed to be held. + /// + /// Some types may provide implementations of this method that are more efficient + /// than simply mapping the `py_clone` method to each element in the view. + #[inline] + fn array_from_view( + py: Python<'_>, + view: ::ndarray::ArrayView<'_, Self, D>, + ) -> ::ndarray::Array + where + D: ::ndarray::Dimension, + { + view.map(|elem| elem.clone_ref(py)) + } } fn npy_int_type_lookup(npy_types: [NPY_TYPES; 3]) -> NPY_TYPES { @@ -796,34 +773,33 @@ fn npy_int_type() -> NPY_TYPES { } } -// Implements `PyClone` for a type that implements `Clone` -macro_rules! impl_py_clone { - ($ty:ty $(; [$param:ident $(: $bound:ident)?])?) => { - impl <$($param$(: $bound)*)?> $crate::dtype::PyClone for $ty { - #[inline] - fn py_clone(&self, _py: ::pyo3::Python<'_>) -> Self { - self.clone() - } +// Invoke within the `Element` impl for a `Clone` type to provide an efficient +// implementation of the cloning methods +macro_rules! clone_methods_impl { + ($Self:ty) => { + #[inline] + fn clone_ref(&self, _py: ::pyo3::Python<'_>) -> $Self { + ::std::clone::Clone::clone(self) + } - #[inline] - fn vec_from_slice(_py: ::pyo3::Python<'_>, slc: &[Self]) -> Vec { - slc.to_owned() - } + #[inline] + fn vec_from_slice(_py: ::pyo3::Python<'_>, slc: &[$Self]) -> Vec<$Self> { + ::std::borrow::ToOwned::to_owned(slc) + } - #[inline] - fn array_from_view( - _py: ::pyo3::Python<'_>, - view: ::ndarray::ArrayView<'_, Self, D> - ) -> ::ndarray::Array - where - D: ::ndarray::Dimension - { - view.to_owned() - } + #[inline] + fn array_from_view( + _py: ::pyo3::Python<'_>, + view: ::ndarray::ArrayView<'_, $Self, D> + ) -> ::ndarray::Array<$Self, D> + where + D: ::ndarray::Dimension + { + ::ndarray::ArrayView::to_owned(&view) } } } -pub(crate) use impl_py_clone; +pub(crate) use clone_methods_impl; macro_rules! impl_element_scalar { (@impl: $ty:ty, $npy_type:expr $(,#[$meta:meta])*) => { @@ -834,8 +810,9 @@ macro_rules! impl_element_scalar { fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> { PyArrayDescr::from_npy_type(py, $npy_type) } + + clone_methods_impl!($ty); } - impl_py_clone!($ty); }; ($ty:ty => $npy_type:ident $(,#[$meta:meta])*) => { impl_element_scalar!(@impl: $ty, NPY_TYPES::$npy_type $(,#[$meta])*); @@ -870,10 +847,9 @@ unsafe impl Element for bf16 { .clone_ref(py) .into_bound(py) } -} -#[cfg(feature = "half")] -impl_py_clone!(bf16); + clone_methods_impl!(Self); +} impl_element_scalar!(Complex32 => NPY_CFLOAT, #[doc = "Complex type with `f32` components which maps to `numpy.csingle` (`numpy.complex64`)."]); @@ -883,19 +859,17 @@ impl_element_scalar!(Complex64 => NPY_CDOUBLE, #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] impl_element_scalar!(usize, isize); -impl PyClone for PyObject { - #[inline] - fn py_clone(&self, py: Python<'_>) -> Self { - self.clone_ref(py) - } -} - unsafe impl Element for PyObject { const IS_COPY: bool = false; fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> { PyArrayDescr::object_bound(py) } + + #[inline] + fn clone_ref(&self, py: Python<'_>) -> Self { + Py::clone_ref(self, py) + } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 90d74b6e5..23be38dba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ pub use crate::convert::{IntoPyArray, NpyIndex, ToNpyDims, ToPyArray}; #[cfg(feature = "gil-refs")] pub use crate::dtype::dtype; pub use crate::dtype::{ - dtype_bound, Complex32, Complex64, Element, PyArrayDescr, PyArrayDescrMethods, PyClone, + dtype_bound, Complex32, Complex64, Element, PyArrayDescr, PyArrayDescrMethods, }; pub use crate::error::{BorrowError, FromVecError, NotContiguousError}; pub use crate::npyffi::{PY_ARRAY_API, PY_UFUNC_API}; diff --git a/src/strings.rs b/src/strings.rs index 72c361b77..3e184d144 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -17,7 +17,7 @@ use pyo3::{ }; use rustc_hash::FxHashMap; -use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods, PyClone}; +use crate::dtype::{clone_methods_impl, Element, PyArrayDescr, PyArrayDescrMethods}; use crate::npyffi::PyDataType_SET_ELSIZE; use crate::npyffi::NPY_TYPES; @@ -82,29 +82,8 @@ unsafe impl Element for PyFixedString { unsafe { DTYPES.from_size(py, NPY_TYPES::NPY_STRING, b'|' as _, size_of::()) } } -} - -impl PyClone for PyFixedString { - #[inline] - fn py_clone(&self, _py: Python<'_>) -> Self { - *self - } - #[inline] - fn vec_from_slice(_py: Python<'_>, slc: &[Self]) -> Vec { - slc.to_owned() - } - - #[inline] - fn array_from_view( - _py: Python<'_>, - view: ::ndarray::ArrayView<'_, Self, D>, - ) -> ::ndarray::Array - where - D: ::ndarray::Dimension, - { - view.to_owned() - } + clone_methods_impl!(Self); } /// A newtype wrapper around [`[PyUCS4; N]`][Py_UCS4] to handle [`str_` scalars][numpy-str] while satisfying coherence. @@ -168,29 +147,6 @@ impl From<[Py_UCS4; N]> for PyFixedUnicode { } } -impl PyClone for PyFixedUnicode { - #[inline] - fn py_clone(&self, _py: Python<'_>) -> Self { - *self - } - - #[inline] - fn vec_from_slice(_py: Python<'_>, slc: &[Self]) -> Vec { - slc.to_owned() - } - - #[inline] - fn array_from_view( - _py: Python<'_>, - view: ::ndarray::ArrayView<'_, Self, D>, - ) -> ::ndarray::Array - where - D: ::ndarray::Dimension, - { - view.to_owned() - } -} - unsafe impl Element for PyFixedUnicode { const IS_COPY: bool = true; @@ -199,6 +155,8 @@ unsafe impl Element for PyFixedUnicode { unsafe { DTYPES.from_size(py, NPY_TYPES::NPY_UNICODE, b'=' as _, size_of::()) } } + + clone_methods_impl!(Self); } struct TypeDescriptors { From eca446044d2a0b1218aace1a04ee5f97e9e35d4a Mon Sep 17 00:00:00 2001 From: Joshua Rudolph Date: Sat, 5 Oct 2024 15:19:09 -0500 Subject: [PATCH 28/31] cargo fmt --- src/datetime.rs | 2 +- src/dtype.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index 8d4b7cfad..50daa269c 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -66,7 +66,7 @@ use std::marker::PhantomData; use pyo3::{sync::GILProtected, Bound, Py, Python}; use rustc_hash::FxHashMap; -use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods, clone_methods_impl}; +use crate::dtype::{clone_methods_impl, Element, PyArrayDescr, PyArrayDescrMethods}; use crate::npyffi::{ PyArray_DatetimeDTypeMetaData, PyDataType_C_METADATA, NPY_DATETIMEUNIT, NPY_TYPES, }; diff --git a/src/dtype.rs b/src/dtype.rs index 31e7cb8c7..abb1d3033 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -5,6 +5,8 @@ use std::ptr; #[cfg(feature = "half")] use half::{bf16, f16}; use num_traits::{Bounded, Zero}; +#[cfg(feature = "half")] +use pyo3::sync::GILOnceCell; use pyo3::{ exceptions::{PyIndexError, PyValueError}, ffi::{self, PyTuple_Size}, @@ -12,8 +14,6 @@ use pyo3::{ types::{PyAnyMethods, PyDict, PyDictMethods, PyTuple, PyType}, Borrowed, Bound, Py, PyAny, PyObject, PyResult, PyTypeInfo, Python, ToPyObject, }; -#[cfg(feature = "half")] -use pyo3::{sync::GILOnceCell}; #[cfg(feature = "gil-refs")] use pyo3::{AsPyPointer, PyNativeType}; @@ -790,14 +790,14 @@ macro_rules! clone_methods_impl { #[inline] fn array_from_view( _py: ::pyo3::Python<'_>, - view: ::ndarray::ArrayView<'_, $Self, D> + view: ::ndarray::ArrayView<'_, $Self, D>, ) -> ::ndarray::Array<$Self, D> where - D: ::ndarray::Dimension + D: ::ndarray::Dimension, { ::ndarray::ArrayView::to_owned(&view) } - } + }; } pub(crate) use clone_methods_impl; From 1c426722cd1f1d2f0efbd922872fa13b9c2cc81f Mon Sep 17 00:00:00 2001 From: Joshua Rudolph Date: Sat, 5 Oct 2024 15:53:19 -0500 Subject: [PATCH 29/31] Added CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3719829e..f6781bb32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Bump MSRV to 1.63. ([#450](https://github.com/PyO3/rust-numpy/pull/450)) - Add `permute` and `transpose` methods for changing the order of axes of a `PyArray`. ([#428](https://github.com/PyO3/rust-numpy/pull/428)) - Add support for NumPy v2 which had a number of changes to the [C API](https://numpy.org/devdocs/numpy_2_0_migration_guide.html#c-api-changes). ([#442](https://github.com/PyO3/rust-numpy/pull/442)) + - Bumped pyo3 dependency to v0.22.0 which required the addition of several new methods to the `Element` trait. ([#435](https://github.com/PyO3/rust-numpy/pull/435)) - v0.21.0 - Migrate to the new `Bound` API introduced by PyO3 0.21. ([#410](https://github.com/PyO3/rust-numpy/pull/410)) ([#411](https://github.com/PyO3/rust-numpy/pull/411)) ([#412](https://github.com/PyO3/rust-numpy/pull/412)) ([#415](https://github.com/PyO3/rust-numpy/pull/415)) ([#416](https://github.com/PyO3/rust-numpy/pull/416)) ([#418](https://github.com/PyO3/rust-numpy/pull/418)) ([#419](https://github.com/PyO3/rust-numpy/pull/419)) ([#420](https://github.com/PyO3/rust-numpy/pull/420)) ([#421](https://github.com/PyO3/rust-numpy/pull/421)) ([#422](https://github.com/PyO3/rust-numpy/pull/422)) From 3e9cad0b6d3f7eedc342c40a0be329f31db9ba63 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sun, 6 Oct 2024 07:03:46 +0100 Subject: [PATCH 30/31] restore deprecation messages --- src/array.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++ src/dtype.rs | 20 ++++++++++++++++++ src/sum_products.rs | 16 ++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/src/array.rs b/src/array.rs index 8c6a63528..73670c158 100644 --- a/src/array.rs +++ b/src/array.rs @@ -229,6 +229,7 @@ impl PyArray { /// assert_eq!(array.bind(py).readonly().as_slice().unwrap(), [0.0; 5]); /// }); /// ``` + #[deprecated(since = "0.21.0", note = "use Bound::unbind() instead")] pub fn to_owned(&self) -> Py { unsafe { Py::from_borrowed_ptr(self.py(), self.as_ptr()) } } @@ -238,6 +239,7 @@ impl PyArray { /// # Safety /// /// This is a wrapper around [`pyo3::FromPyPointer::from_owned_ptr_or_opt`] and inherits its safety contract. + #[deprecated(since = "0.21.0", note = "use Bound::from_owned_ptr() instead")] pub unsafe fn from_owned_ptr<'py>(py: Python<'py>, ptr: *mut ffi::PyObject) -> &'py Self { #![allow(deprecated)] py.from_owned_ptr(ptr) @@ -248,6 +250,7 @@ impl PyArray { /// # Safety /// /// This is a wrapper around [`pyo3::FromPyPointer::from_borrowed_ptr_or_opt`] and inherits its safety contract. + #[deprecated(since = "0.21.0", note = "use Bound::from_borrowed_ptr() instead")] pub unsafe fn from_borrowed_ptr<'py>(py: Python<'py>, ptr: *mut ffi::PyObject) -> &'py Self { #![allow(deprecated)] py.from_borrowed_ptr(ptr) @@ -577,6 +580,10 @@ impl PyArray { /// /// # Safety /// Same as [`PyArray::new_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `PyArray::new_bound` in the future" + )] pub unsafe fn new<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> &Self where ID: IntoDimension, @@ -588,6 +595,10 @@ impl PyArray { /// /// # Safety /// Same as [`PyArray::borrow_from_array_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `PyArray::borrow_from_array_bound` in the future" + )] pub unsafe fn borrow_from_array<'py, S>( array: &ArrayBase, container: &'py PyAny, @@ -599,6 +610,10 @@ impl PyArray { } /// Deprecated form of [`PyArray::zeros_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `PyArray::zeros_bound` in the future" + )] pub fn zeros<'py, ID>(py: Python<'py>, dims: ID, is_fortran: bool) -> &Self where ID: IntoDimension, @@ -641,6 +656,10 @@ impl PyArray { } /// Deprecated form of [`PyArray::from_owned_array_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by PyArray::from_owned_array_bound in the future" + )] pub fn from_owned_array<'py>(py: Python<'py>, arr: Array) -> &'py Self { Self::from_owned_array_bound(py, arr).into_gil_ref() } @@ -821,6 +840,10 @@ impl PyArray { } /// Deprecated form of [`PyArray::from_array_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by PyArray::from_array_bound in the future" + )] pub fn from_array<'py, S>(py: Python<'py>, arr: &ArrayBase) -> &'py Self where S: Data, @@ -981,6 +1004,10 @@ where impl PyArray { /// Deprecated form of [`PyArray::from_owned_object_array_bound`] #[cfg(feature = "gil-refs")] + #[deprecated( + since = "0.21.0", + note = "will be replaced by PyArray::from_owned_object_array_bound in the future" + )] pub fn from_owned_object_array<'py, T>(py: Python<'py>, arr: Array, D>) -> &'py Self { Self::from_owned_object_array_bound(py, arr).into_gil_ref() } @@ -1119,17 +1146,29 @@ impl PyArray { #[cfg(feature = "gil-refs")] impl PyArray { /// Deprecated form of [`PyArray::from_slice_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `PyArray::from_slice_bound` in the future" + )] pub fn from_slice<'py>(py: Python<'py>, slice: &[T]) -> &'py Self { Self::from_slice_bound(py, slice).into_gil_ref() } /// Deprecated form of [`PyArray::from_vec_bound`] #[inline(always)] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `PyArray::from_vec_bound` in the future" + )] pub fn from_vec<'py>(py: Python<'py>, vec: Vec) -> &'py Self { Self::from_vec_bound(py, vec).into_gil_ref() } /// Deprecated form of [`PyArray::from_iter_bound`] + #[deprecated( + since = "0.21.0", + note = "will be replaced by PyArray::from_iter_bound in the future" + )] pub fn from_iter<'py, I>(py: Python<'py>, iter: I) -> &'py Self where I: IntoIterator, @@ -1141,6 +1180,10 @@ impl PyArray { impl PyArray { /// Deprecated form of [`PyArray::from_vec2_bound`] #[cfg(feature = "gil-refs")] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `PyArray::from_vec2_bound` in the future" + )] pub fn from_vec2<'py>(py: Python<'py>, v: &[Vec]) -> Result<&'py Self, FromVecError> { Self::from_vec2_bound(py, v).map(Bound::into_gil_ref) } @@ -1191,6 +1234,10 @@ impl PyArray { impl PyArray { /// Deprecated form of [`PyArray::from_vec3_bound`] #[cfg(feature = "gil-refs")] + #[deprecated( + since = "0.21.0", + note = "will be replaced by `PyArray::from_vec3_bound` in the future" + )] pub fn from_vec3<'py>(py: Python<'py>, v: &[Vec>]) -> Result<&'py Self, FromVecError> { Self::from_vec3_bound(py, v).map(Bound::into_gil_ref) } @@ -1428,6 +1475,10 @@ impl PyArray { impl> PyArray { /// Deprecated form of [`PyArray::arange_bound`] #[cfg(feature = "gil-refs")] + #[deprecated( + since = "0.21.0", + note = "will be replaced by PyArray::arange_bound in the future" + )] pub fn arange<'py>(py: Python<'py>, start: T, stop: T, step: T) -> &Self { Self::arange_bound(py, start, stop, step).into_gil_ref() } diff --git a/src/dtype.rs b/src/dtype.rs index abb1d3033..b6cbea0b4 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -71,6 +71,10 @@ pyobject_native_type_extract!(PyArrayDescr); /// Returns the type descriptor ("dtype") for a registered type. #[cfg(feature = "gil-refs")] +#[deprecated( + since = "0.21.0", + note = "This will be replaced by `dtype_bound` in the future." +)] pub fn dtype<'py, T: Element>(py: Python<'py>) -> &'py PyArrayDescr { T::get_dtype_bound(py).into_gil_ref() } @@ -136,6 +140,10 @@ impl PyArrayDescr { /// Equivalent to invoking the constructor of [`numpy.dtype`][dtype]. /// /// [dtype]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.html + #[deprecated( + since = "0.21.0", + note = "This will be replace by `new_bound` in the future." + )] pub fn new<'py, T: ToPyObject + ?Sized>(py: Python<'py>, ob: &T) -> PyResult<&'py Self> { Self::new_bound(py, ob).map(Bound::into_gil_ref) } @@ -153,11 +161,19 @@ impl PyArrayDescr { } /// Shortcut for creating a type descriptor of `object` type. + #[deprecated( + since = "0.21.0", + note = "This will be replaced by `object_bound` in the future." + )] pub fn object<'py>(py: Python<'py>) -> &'py Self { Self::object_bound(py).into_gil_ref() } /// Returns the type descriptor for a registered type. + #[deprecated( + since = "0.21.0", + note = "This will be replaced by `of_bound` in the future." + )] pub fn of<'py, T: Element>(py: Python<'py>) -> &'py Self { Self::of_bound::(py).into_gil_ref() } @@ -693,6 +709,10 @@ pub unsafe trait Element: Sized + Send { /// Returns the associated type descriptor ("dtype") for the given element type. #[cfg(feature = "gil-refs")] + #[deprecated( + since = "0.21.0", + note = "This will be replaced by `get_dtype_bound` in the future." + )] fn get_dtype<'py>(py: Python<'py>) -> &'py PyArrayDescr { Self::get_dtype_bound(py).into_gil_ref() } diff --git a/src/sum_products.rs b/src/sum_products.rs index 3dd4a206e..0101f7212 100644 --- a/src/sum_products.rs +++ b/src/sum_products.rs @@ -33,6 +33,10 @@ where impl<'py, T> ArrayOrScalar<'py, T> for T where T: Element + FromPyObject<'py> {} /// Deprecated form of [`inner_bound`] +#[deprecated( + since = "0.21.0", + note = "will be replaced by `inner_bound` in the future" +)] #[cfg(feature = "gil-refs")] pub fn inner<'py, T, DIN1, DIN2, OUT>( array1: &'py PyArray, @@ -101,6 +105,10 @@ where /// Deprecated form of [`dot_bound`] #[cfg(feature = "gil-refs")] +#[deprecated( + since = "0.21.0", + note = "will be replaced by `dot_bound` in the future" +)] pub fn dot<'py, T, DIN1, DIN2, OUT>( array1: &'py PyArray, array2: &'py PyArray, @@ -174,6 +182,10 @@ where /// Deprecated form of [`einsum_bound`] #[cfg(feature = "gil-refs")] +#[deprecated( + since = "0.21.0", + note = "will be replaced by `einsum_bound` in the future" +)] pub fn einsum<'py, T, OUT>(subscripts: &str, arrays: &[&'py PyArray]) -> PyResult where T: Element, @@ -221,6 +233,10 @@ where /// Deprecated form of [`einsum_bound!`][crate::einsum_bound!] #[cfg(feature = "gil-refs")] +#[deprecated( + since = "0.21.0", + note = "will be replaced by `einsum_bound!` in the future" +)] #[macro_export] macro_rules! einsum { ($subscripts:literal $(,$array:ident)+ $(,)*) => {{ From b17fcba7898f00f0d2323d5a7ba93cd442486dd1 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sun, 6 Oct 2024 07:05:57 +0100 Subject: [PATCH 31/31] restore allow deprecated --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 23be38dba..2b2d1f088 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,6 +112,7 @@ pub use crate::borrow::{ }; pub use crate::convert::{IntoPyArray, NpyIndex, ToNpyDims, ToPyArray}; #[cfg(feature = "gil-refs")] +#[allow(deprecated)] pub use crate::dtype::dtype; pub use crate::dtype::{ dtype_bound, Complex32, Complex64, Element, PyArrayDescr, PyArrayDescrMethods, @@ -120,6 +121,7 @@ pub use crate::error::{BorrowError, FromVecError, NotContiguousError}; pub use crate::npyffi::{PY_ARRAY_API, PY_UFUNC_API}; pub use crate::strings::{PyFixedString, PyFixedUnicode}; #[cfg(feature = "gil-refs")] +#[allow(deprecated)] pub use crate::sum_products::{dot, einsum, inner}; pub use crate::sum_products::{dot_bound, einsum_bound, inner_bound}; pub use crate::untyped_array::{PyUntypedArray, PyUntypedArrayMethods};