@@ -6,10 +6,15 @@ use pyo3::{
6
6
ffi, prelude:: * , type_object, types:: PyAny , AsPyPointer , PyDowncastError , PyNativeType ,
7
7
PyResult ,
8
8
} ;
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
+ } ;
10
15
use std:: { iter:: ExactSizeIterator , marker:: PhantomData } ;
11
16
12
- use crate :: convert:: { IntoPyArray , NpyIndex , ToNpyDims , ToPyArray } ;
17
+ use crate :: convert:: { ArrayExt , IntoPyArray , NpyIndex , ToNpyDims , ToPyArray } ;
13
18
use crate :: dtype:: { DataType , Element } ;
14
19
use crate :: error:: { FromVecError , NotContiguousError , ShapeError } ;
15
20
use crate :: slice_box:: SliceBox ;
@@ -468,6 +473,65 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
468
473
Self :: from_owned_ptr ( py, ptr)
469
474
}
470
475
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
+
471
535
/// Construct a new nd-dimensional array filled with 0.
472
536
///
473
537
/// If `is_fortran` is true, then
0 commit comments