@@ -6,10 +6,15 @@ use pyo3::{
66 ffi, prelude:: * , type_object, types:: PyAny , AsPyPointer , PyDowncastError , PyNativeType ,
77 PyResult ,
88} ;
9- use std:: { cell:: Cell , mem, os:: raw:: c_int, ptr, slice} ;
9+ use std:: {
10+ cell:: Cell ,
11+ mem,
12+ os:: raw:: { c_int, c_void} ,
13+ ptr, slice,
14+ } ;
1015use std:: { iter:: ExactSizeIterator , marker:: PhantomData } ;
1116
12- use crate :: convert:: { IntoPyArray , NpyIndex , ToNpyDims , ToPyArray } ;
17+ use crate :: convert:: { ArrayExt , IntoPyArray , NpyIndex , ToNpyDims , ToPyArray } ;
1318use crate :: dtype:: { DataType , Element } ;
1419use crate :: error:: { FromVecError , NotContiguousError , ShapeError } ;
1520use crate :: slice_box:: SliceBox ;
@@ -468,6 +473,65 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
468473 Self :: from_owned_ptr ( py, ptr)
469474 }
470475
476+ /// Creates a NumPy array backed by `array` and ties its ownership to the Python object `owner`.
477+ ///
478+ /// # Safety
479+ ///
480+ /// `owner` is set as a base object of the returned array which must not be dropped until `owner` is dropped.
481+ /// Furthermore, `array` must not be reallocated from the time this method is called and until `owner` is dropped.
482+ ///
483+ /// # Example
484+ ///
485+ /// ```rust
486+ /// # use pyo3::prelude::*;
487+ /// # use numpy::{ndarray::Array1, PyArray1};
488+ /// #
489+ /// #[pyclass]
490+ /// struct Owner {
491+ /// array: Array1<f64>,
492+ /// }
493+ ///
494+ /// #[pymethods]
495+ /// impl Owner {
496+ /// #[getter]
497+ /// fn array<'py>(this: &'py PyCell<Self>) -> &'py PyArray1<f64> {
498+ /// let array = &this.borrow().array;
499+ ///
500+ /// // SAFETY: The memory backing `array` will stay valid as long as this object is alive
501+ /// // as we do not modify `array` in any way which would cause it to be reallocated.
502+ /// unsafe { PyArray1::borrow_from_array(array, this) }
503+ /// }
504+ /// }
505+ /// ```
506+ pub unsafe fn borrow_from_array < ' py , S > ( array : & ArrayBase < S , D > , owner : & ' py PyAny ) -> & ' py Self
507+ where
508+ S : Data < Elem = T > ,
509+ {
510+ let ( strides, dims) = ( array. npy_strides ( ) , array. raw_dim ( ) ) ;
511+ let data_ptr = array. as_ptr ( ) ;
512+
513+ let ptr = PY_ARRAY_API . PyArray_New (
514+ PY_ARRAY_API . get_type_object ( npyffi:: NpyTypes :: PyArray_Type ) ,
515+ dims. ndim_cint ( ) ,
516+ dims. as_dims_ptr ( ) ,
517+ T :: npy_type ( ) as c_int ,
518+ strides. as_ptr ( ) as * mut npy_intp , // strides
519+ data_ptr as * mut c_void , // data
520+ mem:: size_of :: < T > ( ) as c_int , // itemsize
521+ 0 , // flag
522+ ptr:: null_mut ( ) , // obj
523+ ) ;
524+
525+ mem:: forget ( owner. to_object ( owner. py ( ) ) ) ;
526+
527+ PY_ARRAY_API . PyArray_SetBaseObject (
528+ ptr as * mut npyffi:: PyArrayObject ,
529+ owner as * const PyAny as * mut PyAny as * mut ffi:: PyObject ,
530+ ) ;
531+
532+ Self :: from_owned_ptr ( owner. py ( ) , ptr)
533+ }
534+
471535 /// Construct a new nd-dimensional array filled with 0.
472536 ///
473537 /// If `is_fortran` is true, then
0 commit comments