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)) diff --git a/Cargo.toml b/Cargo.toml index b634e8720..b467b7ba3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,15 @@ 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] +gil-refs = ["pyo3/gil-refs"] 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" ``` 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] 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` diff --git a/src/array.rs b/src/array.rs index 76d62d1c3..73670c158 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. /// @@ -234,7 +241,7 @@ impl PyArray { /// 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)] + #![allow(deprecated)] py.from_owned_ptr(ptr) } @@ -245,7 +252,7 @@ impl PyArray { /// 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)] + #![allow(deprecated)] py.from_borrowed_ptr(ptr) } @@ -287,27 +294,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 +418,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 +469,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 +511,116 @@ 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`] + #[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() + } + + /// 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() + } + + /// 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() + } + /// Returns an immutable view of the internal data as a slice. /// /// # Safety @@ -598,37 +664,6 @@ impl PyArray { 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 @@ -816,31 +851,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()) @@ -936,6 +946,7 @@ impl PyArray { } #[cfg(feature = "nalgebra")] +#[cfg(feature = "gil-refs")] impl PyArray where N: nalgebra::Scalar + Element, @@ -992,6 +1003,7 @@ 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" @@ -1053,6 +1065,7 @@ impl PyArray { } } +#[cfg(feature = "gil-refs")] impl PyArray { /// Get the single element of a zero-dimensional array. /// @@ -1063,15 +1076,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 @@ -1090,21 +1094,11 @@ 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 } } - /// 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 +1118,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,8 +1143,43 @@ 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, + { + Self::from_iter_bound(py, iter).into_gil_ref() + } +} + 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" @@ -1207,7 +1224,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) } @@ -1216,6 +1233,7 @@ 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" @@ -1275,7 +1293,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) @@ -1283,6 +1301,7 @@ impl PyArray { } } +#[cfg(feature = "gil-refs")] impl PyArray { /// Copies `self` into `other`, performing a data type conversion if necessary. /// @@ -1455,6 +1474,7 @@ 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" @@ -1498,13 +1518,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.clone_ref(py)); *data_ptr = data_ptr.add(1); } } @@ -1718,10 +1738,7 @@ pub trait PyArrayMethods<'py, T, D>: 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> @@ -1752,10 +1769,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> @@ -1859,10 +1873,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. /// @@ -2238,10 +2249,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.clone_ref(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, @@ -2294,6 +2324,15 @@ impl<'py, T, D> PyArrayMethods<'py, T, D> for Bound<'py, PyArray> { }) } + 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<()> where T: Element, 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/convert.rs b/src/convert.rs index 4e50c5126..a58d5c427 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -45,6 +45,7 @@ pub trait IntoPyArray: Sized { 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() } @@ -156,6 +157,7 @@ pub trait ToPyArray { 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() } @@ -201,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.clone()); + data_ptr.write(item.clone_ref(py)); data_ptr = data_ptr.add(1); } array @@ -234,7 +236,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.clone_ref(py)); data_ptr = data_ptr.add(1); } } diff --git a/src/datetime.rs b/src/datetime.rs index 794170616..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}; +use crate::dtype::{clone_methods_impl, Element, PyArrayDescr, PyArrayDescrMethods}; use crate::npyffi::{ PyArray_DatetimeDTypeMetaData, PyDataType_C_METADATA, NPY_DATETIMEUNIT, NPY_TYPES, }; @@ -163,6 +163,8 @@ unsafe impl Element for Datetime { DTYPES.from_unit(py, U::UNIT) } + + clone_methods_impl!(Self); } impl fmt::Debug for Datetime { @@ -198,6 +200,8 @@ unsafe impl Element for Timedelta { DTYPES.from_unit(py, U::UNIT) } + + clone_methods_impl!(Self); } impl fmt::Debug for Timedelta { @@ -243,7 +247,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..b6cbea0b4 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -5,16 +5,17 @@ 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}, 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, Py, 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,6 +70,7 @@ unsafe impl PyTypeInfo for PyArrayDescr { 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." @@ -82,18 +85,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 +108,46 @@ 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 + #[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) + } + /// Returns `self` as `*mut PyArray_Descr`. pub fn as_dtype_ptr(&self) -> *mut PyArray_Descr { self.as_borrowed().as_dtype_ptr() @@ -138,11 +169,6 @@ impl PyArrayDescr { 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", @@ -152,30 +178,11 @@ impl PyArrayDescr { 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]. @@ -690,7 +697,7 @@ impl Sealed for Bound<'_, PyArrayDescr> {} /// /// [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: 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 @@ -701,6 +708,7 @@ pub unsafe trait Element: Clone + Send { const IS_COPY: bool; /// 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." @@ -711,6 +719,33 @@ pub unsafe trait Element: Clone + 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 { @@ -758,6 +793,34 @@ fn npy_int_type() -> NPY_TYPES { } } +// 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<$Self> { + ::std::borrow::ToOwned::to_owned(slc) + } + + #[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 clone_methods_impl; + macro_rules! impl_element_scalar { (@impl: $ty:ty, $npy_type:expr $(,#[$meta:meta])*) => { $(#[$meta])* @@ -767,6 +830,8 @@ macro_rules! impl_element_scalar { fn get_dtype_bound(py: Python<'_>) -> Bound<'_, PyArrayDescr> { PyArrayDescr::from_npy_type(py, $npy_type) } + + clone_methods_impl!($ty); } }; ($ty:ty => $npy_type:ident $(,#[$meta:meta])*) => { @@ -799,9 +864,11 @@ 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) } + + clone_methods_impl!(Self); } impl_element_scalar!(Complex32 => NPY_CFLOAT, @@ -818,12 +885,18 @@ unsafe impl Element for PyObject { 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)] mod tests { use super::*; + use pyo3::types::PyString; use pyo3::{py_run, types::PyTypeMethods}; use crate::npyffi::{is_numpy_2, NPY_NEEDS_PYAPI}; @@ -851,7 +924,7 @@ mod tests { #[test] fn test_dtype_names() { - fn type_name<'py, T: Element>(py: Python<'py>) -> String { + 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 44dc09622..2b2d1f088 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; //! @@ -65,12 +65,14 @@ 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 - -#![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 +#![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"); @@ -109,6 +111,7 @@ pub use crate::borrow::{ PyReadwriteArray5, PyReadwriteArray6, PyReadwriteArrayDyn, }; pub use crate::convert::{IntoPyArray, NpyIndex, ToNpyDims, ToPyArray}; +#[cfg(feature = "gil-refs")] #[allow(deprecated)] pub use crate::dtype::dtype; pub use crate::dtype::{ @@ -117,6 +120,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}; +#[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}; diff --git a/src/strings.rs b/src/strings.rs index 1f440fbda..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}; +use crate::dtype::{clone_methods_impl, Element, PyArrayDescr, PyArrayDescrMethods}; use crate::npyffi::PyDataType_SET_ELSIZE; use crate::npyffi::NPY_TYPES; @@ -82,6 +82,8 @@ unsafe impl Element for PyFixedString { unsafe { DTYPES.from_size(py, NPY_TYPES::NPY_STRING, b'|' as _, size_of::()) } } + + clone_methods_impl!(Self); } /// A newtype wrapper around [`[PyUCS4; N]`][Py_UCS4] to handle [`str_` scalars][numpy-str] while satisfying coherence. @@ -153,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 { @@ -191,7 +195,7 @@ impl TypeDescriptors { } }; - dtype.clone().into_bound(py) + dtype.bind(py).to_owned() } } diff --git a/src/sum_products.rs b/src/sum_products.rs index 32cff6203..0101f7212 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, @@ -34,6 +37,7 @@ impl<'py, T> ArrayOrScalar<'py, T> for T where T: Element + FromPyObject<'py> {} 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,6 +104,7 @@ 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" @@ -176,6 +181,7 @@ 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" @@ -226,6 +232,7 @@ 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" 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] diff --git a/tests/array.rs b/tests/array.rs index 273173c79..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) @@ -259,6 +261,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 +276,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 +298,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 +316,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 +334,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 +481,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]);