diff --git a/src/array.rs b/src/array.rs index 0f734c11f..f9a88d4e9 100644 --- a/src/array.rs +++ b/src/array.rs @@ -129,7 +129,7 @@ unsafe impl PyTypeInfo for PyArray { const MODULE: Option<&'static str> = Some("numpy"); fn type_object_raw<'py>(py: Python<'py>) -> *mut ffi::PyTypeObject { - unsafe { npyffi::PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type) } + unsafe { npyffi::get_type_object(py, npyffi::NpyTypes::PyArray_Type) } } fn is_type_of(ob: &Bound<'_, PyAny>) -> bool { @@ -233,7 +233,7 @@ impl PyArray { let mut dims = dims.into_dimension(); let ptr = PY_ARRAY_API.PyArray_NewFromDescr( py, - PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type), + npyffi::get_type_object(py, npyffi::NpyTypes::PyArray_Type), T::get_dtype(py).into_dtype_ptr(), dims.ndim_cint(), dims.as_dims_ptr(), @@ -259,7 +259,7 @@ impl PyArray { let mut dims = dims.into_dimension(); let ptr = PY_ARRAY_API.PyArray_NewFromDescr( py, - PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type), + npyffi::get_type_object(py, npyffi::NpyTypes::PyArray_Type), T::get_dtype(py).into_dtype_ptr(), dims.ndim_cint(), dims.as_dims_ptr(), diff --git a/src/dtype.rs b/src/dtype.rs index 3e04de7b9..88e112ead 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -17,7 +17,7 @@ use pyo3::{ }; use crate::npyffi::{ - NpyTypes, PyArray_Descr, PyDataType_ALIGNMENT, PyDataType_ELSIZE, PyDataType_FIELDS, + self, NpyTypes, PyArray_Descr, PyDataType_ALIGNMENT, PyDataType_ELSIZE, PyDataType_FIELDS, PyDataType_FLAGS, PyDataType_NAMES, PyDataType_SUBARRAY, NPY_ALIGNED_STRUCT, NPY_BYTEORDER_CHAR, NPY_ITEM_HASOBJECT, NPY_TYPES, PY_ARRAY_API, }; @@ -58,7 +58,7 @@ unsafe impl PyTypeInfo for PyArrayDescr { #[inline] fn type_object_raw<'py>(py: Python<'py>) -> *mut ffi::PyTypeObject { - unsafe { PY_ARRAY_API.get_type_object(py, NpyTypes::PyArrayDescr_Type) } + unsafe { npyffi::get_type_object(py, NpyTypes::PyArrayDescr_Type) } } } diff --git a/src/npyffi/array.rs b/src/npyffi/array.rs index c81a0550a..384fcfa96 100644 --- a/src/npyffi/array.rs +++ b/src/npyffi/array.rs @@ -4,10 +4,11 @@ //! The reason is that they would be re-exports of the `PyMem_Raw{Malloc,Realloc,Free}` functions from PyO3, //! but those are not unconditionally exported, i.e. they are not available when using the limited Python C-API. -use std::os::raw::*; +use std::{os::raw::*, ptr::NonNull}; use libc::FILE; use pyo3::{ + exceptions::PyRuntimeError, ffi::{self, PyObject, PyTypeObject}, sync::PyOnceLock, }; @@ -75,17 +76,68 @@ const CAPSULE_NAME: &str = "_ARRAY_API"; pub static PY_ARRAY_API: PyArrayAPI = PyArrayAPI(PyOnceLock::new()); /// See [PY_ARRAY_API] for more. -pub struct PyArrayAPI(PyOnceLock<*const *const c_void>); +pub struct PyArrayAPI(PyOnceLock>); unsafe impl Send for PyArrayAPI {} unsafe impl Sync for PyArrayAPI {} impl PyArrayAPI { - unsafe fn get<'py>(&self, py: Python<'py>, offset: isize) -> *const *const c_void { + pub(super) unsafe fn get<'py>(&self, py: Python<'py>, offset: isize) -> NonNull<*const c_void> { let api = self .0 - .get_or_try_init(py, || get_numpy_api(py, mod_name(py)?, CAPSULE_NAME)) + .get_or_try_init(py, || -> PyResult<_> { + let api = get_numpy_api(py, mod_name(py)?, CAPSULE_NAME)?; + + let module_version = { + // unsigned int PyArray_GetNDArrayCVersion(); + let get_abi_version: extern "C" fn() -> c_uint = + api.add(0).cast().read(); + get_abi_version() + }; + if NPY_VERSION < module_version { + return Err(PyRuntimeError::new_err(format!( + "module compiled against ABI version 0x{:x} but this version of numpy is 0x{:x}", + NPY_VERSION, module_version + ))); + } + + let module_feature_version = unsafe { + // unsigned int PyArray_GetNDArrayCFeatureVersion(); + let get_runtime_version: extern "C" fn() -> c_uint = + api.add(211).cast().read(); + get_runtime_version() + }; + if NPY_FEATURE_VERSION > module_feature_version { + return Err(PyRuntimeError::new_err(format!( + "module was compiled against NumPy C-API version 0x{:x} (NumPy {}) but the running NumPy has C-API version 0x{:x}", + NPY_FEATURE_VERSION, NPY_FEATURE_VERSION_STRING, module_feature_version + ))); + } + + let endianess = unsafe { + // int PyArray_GetEndianness(); + let get_endianess: extern "C" fn() -> c_int = + api.add(210).cast().read(); + get_endianess() + }; + + #[cfg(target_endian = "big")] + if endianess != NPY_CPU_BIG { + return Err(PyRuntimeError::new_err( + "module compiled as big endian, but detected different endianess at runtime", + )); + } + + #[cfg(target_endian = "little")] + if endianess != NPY_CPU_LITTLE { + return Err(PyRuntimeError::new_err( + "module compiled as little endian, but detected different endianess at runtime", + )); + } + + Ok(api) + }) .expect("Failed to access NumPy array API capsule"); api.offset(offset) @@ -94,8 +146,8 @@ impl PyArrayAPI { impl PyArrayAPI { impl_api![0; PyArray_GetNDArrayCVersion() -> c_uint]; - impl_api![40; PyArray_SetNumericOps(dict: *mut PyObject) -> c_int]; - impl_api![41; PyArray_GetNumericOps() -> *mut PyObject]; + // Unused slot 40, was `PyArray_SetNumericOps` + // Unused slot 41, was `PyArray_GetNumericOps`, impl_api![42; PyArray_INCREF(mp: *mut PyArrayObject) -> c_int]; impl_api![43; PyArray_XDECREF(mp: *mut PyArrayObject) -> c_int]; impl_api![44; PyArray_SetStringFunction(op: *mut PyObject, repr: c_int)]; @@ -104,7 +156,8 @@ impl PyArrayAPI { impl_api![47; PyArray_Zero(arr: *mut PyArrayObject) -> *mut c_char]; impl_api![48; PyArray_One(arr: *mut PyArrayObject) -> *mut c_char]; impl_api![49; PyArray_CastToType(arr: *mut PyArrayObject, dtype: *mut PyArray_Descr, is_f_order: c_int) -> *mut PyObject]; - impl_api![50; NumPy1; PyArray_CastTo(out: *mut PyArrayObject, mp: *mut PyArrayObject) -> c_int]; + impl_api![50; PyArray_CopyInto(dest: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; + impl_api![51; PyArray_CopyAnyInto(dest: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; impl_api![52; PyArray_CanCastSafely(fromtype: c_int, totype: c_int) -> c_int]; impl_api![53; PyArray_CanCastTo(from: *mut PyArray_Descr, to: *mut PyArray_Descr) -> npy_bool]; impl_api![54; PyArray_ObjectType(op: *mut PyObject, minimum_type: c_int) -> c_int]; @@ -118,11 +171,10 @@ impl PyArrayAPI { impl_api![62; PyArray_ScalarAsCtype(scalar: *mut PyObject, ctypeptr: *mut c_void)]; impl_api![63; PyArray_CastScalarToCtype(scalar: *mut PyObject, ctypeptr: *mut c_void, outcode: *mut PyArray_Descr) -> c_int]; impl_api![64; PyArray_CastScalarDirect(scalar: *mut PyObject, indescr: *mut PyArray_Descr, ctypeptr: *mut c_void, outtype: c_int) -> c_int]; - impl_api![65; NumPy1; PyArray_ScalarFromObject(object: *mut PyObject) -> *mut PyObject]; - impl_api![65; NumPy2; PyArray_Pack(descr: *mut PyArray_Descr, item: *mut c_void, value: *const PyObject) -> *mut PyObject]; - impl_api![66; NumPy1; PyArray_GetCastFunc(descr: *mut PyArray_Descr, type_num: c_int) -> PyArray_VectorUnaryFunc]; - impl_api![67; NumPy1; PyArray_FromDims(nd: c_int, d: *mut c_int, type_: c_int) -> *mut PyObject]; - impl_api![68; NumPy1; PyArray_FromDimsAndDataAndDescr(nd: c_int, d: *mut c_int, descr: *mut PyArray_Descr, data: *mut c_char) -> *mut PyObject]; + // Min v2.0 impl_api![65; PyArray_Pack(descr: *mut PyArray_Descr, iter: *mut c_void, value: *mut PyObject) -> c_int]; + // Unused slot 66, was `PyArray_GetCastFunc` + // Unused slot 67, was `PyArray_FromDims` + // Unused slot 68, was `PyArray_FromDimsAndDataAndDescr` impl_api![69; PyArray_FromAny(op: *mut PyObject, newtype: *mut PyArray_Descr, min_depth: c_int, max_depth: c_int, flags: c_int, context: *mut PyObject) -> *mut PyObject]; impl_api![70; PyArray_EnsureArray(op: *mut PyObject) -> *mut PyObject]; impl_api![71; PyArray_EnsureAnyArray(op: *mut PyObject) -> *mut PyObject]; @@ -135,7 +187,9 @@ impl PyArrayAPI { impl_api![78; PyArray_SetField(self_: *mut PyArrayObject, dtype: *mut PyArray_Descr, offset: c_int, val: *mut PyObject) -> c_int]; impl_api![79; PyArray_Byteswap(self_: *mut PyArrayObject, inplace: npy_bool) -> *mut PyObject]; impl_api![80; PyArray_Resize(self_: *mut PyArrayObject, newshape: *mut PyArray_Dims, refcheck: c_int, order: NPY_ORDER) -> *mut PyObject]; - impl_api![81; NumPy1; PyArray_MoveInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; + // Unused slot 81, was `PyArray_MoveInto`` + // Unused slot 82 was `PyArray_CopyInto` (which replaces `..._CastTo`) + // Unused slot 82 was `PyArray_CopyAnyInto` (which replaces `..._CastAnyTo`) impl_api![84; PyArray_CopyObject(dest: *mut PyArrayObject, src_object: *mut PyObject) -> c_int]; impl_api![85; PyArray_NewCopy(obj: *mut PyArrayObject, order: NPY_ORDER) -> *mut PyObject]; impl_api![86; PyArray_ToList(self_: *mut PyArrayObject) -> *mut PyObject]; @@ -155,7 +209,7 @@ impl PyArrayAPI { impl_api![100; PyArray_PyIntAsInt(o: *mut PyObject) -> c_int]; impl_api![101; PyArray_PyIntAsIntp(o: *mut PyObject) -> npy_intp]; impl_api![102; PyArray_Broadcast(mit: *mut PyArrayMultiIterObject) -> c_int]; - impl_api![103; NumPy1; PyArray_FillObjectArray(arr: *mut PyArrayObject, obj: *mut PyObject)]; + // Unused slot 103, was `PyArray_FillObjectArray` impl_api![104; PyArray_FillWithScalar(arr: *mut PyArrayObject, obj: *mut PyObject) -> c_int]; impl_api![105; PyArray_CheckStrides(elsize: c_int, nd: c_int, numbytes: npy_intp, offset: npy_intp, dims: *mut npy_intp, newstrides: *mut npy_intp) -> npy_bool]; impl_api![106; PyArray_DescrNewByteorder(self_: *mut PyArray_Descr, newendian: c_char) -> *mut PyArray_Descr]; @@ -167,14 +221,14 @@ impl PyArrayAPI { impl_api![112; PyArray_FromArrayAttr(op: *mut PyObject, typecode: *mut PyArray_Descr, context: *mut PyObject) -> *mut PyObject]; impl_api![113; PyArray_ScalarKind(typenum: c_int, arr: *mut *mut PyArrayObject) -> NPY_SCALARKIND]; impl_api![114; PyArray_CanCoerceScalar(thistype: c_int, neededtype: c_int, scalar: NPY_SCALARKIND) -> c_int]; - impl_api![115; NumPy1; PyArray_NewFlagsObject(obj: *mut PyObject) -> *mut PyObject]; + // Unused slot 115, was `PyArray_NewFlagsObject` impl_api![116; PyArray_CanCastScalar(from: *mut PyTypeObject, to: *mut PyTypeObject) -> npy_bool]; - impl_api![117; NumPy1; PyArray_CompareUCS4(s1: *mut npy_ucs4, s2: *mut npy_ucs4, len: usize) -> c_int]; + // Unused slot 117, was `PyArray_CompareUCS4` impl_api![118; PyArray_RemoveSmallest(multi: *mut PyArrayMultiIterObject) -> c_int]; impl_api![119; PyArray_ElementStrides(obj: *mut PyObject) -> c_int]; impl_api![120; PyArray_Item_INCREF(data: *mut c_char, descr: *mut PyArray_Descr)]; impl_api![121; PyArray_Item_XDECREF(data: *mut c_char, descr: *mut PyArray_Descr)]; - impl_api![122; NumPy1; PyArray_FieldNames(fields: *mut PyObject) -> *mut PyObject]; + // Unused slot 122, was `PyArray_FieldNames` impl_api![123; PyArray_Transpose(ap: *mut PyArrayObject, permute: *mut PyArray_Dims) -> *mut PyObject]; impl_api![124; PyArray_TakeFrom(self0: *mut PyArrayObject, indices0: *mut PyObject, axis: c_int, out: *mut PyArrayObject, clipmode: NPY_CLIPMODE) -> *mut PyObject]; impl_api![125; PyArray_PutTo(self_: *mut PyArrayObject, values0: *mut PyObject, indices0: *mut PyObject, clipmode: NPY_CLIPMODE) -> *mut PyObject]; @@ -215,17 +269,17 @@ impl PyArrayAPI { impl_api![160; PyArray_GetPtr(obj: *mut PyArrayObject, ind: *mut npy_intp) -> *mut c_void]; impl_api![161; PyArray_CompareLists(l1: *mut npy_intp, l2: *mut npy_intp, n: c_int) -> c_int]; impl_api![162; PyArray_AsCArray(op: *mut *mut PyObject, ptr: *mut c_void, dims: *mut npy_intp, nd: c_int, typedescr: *mut PyArray_Descr) -> c_int]; - impl_api![163; NumPy1; PyArray_As1D(op: *mut *mut PyObject, ptr: *mut *mut c_char, d1: *mut c_int, typecode: c_int) -> c_int]; - impl_api![164; NumPy1; PyArray_As2D(op: *mut *mut PyObject, ptr: *mut *mut *mut c_char, d1: *mut c_int, d2: *mut c_int, typecode: c_int) -> c_int]; + // Unused slot 163, was `PyArray_As1D` + // Unused slot 164, was `PyArray_As2D` impl_api![165; PyArray_Free(op: *mut PyObject, ptr: *mut c_void) -> c_int]; impl_api![166; PyArray_Converter(object: *mut PyObject, address: *mut *mut PyObject) -> c_int]; impl_api![167; PyArray_IntpFromSequence(seq: *mut PyObject, vals: *mut npy_intp, maxvals: c_int) -> c_int]; impl_api![168; PyArray_Concatenate(op: *mut PyObject, axis: c_int) -> *mut PyObject]; impl_api![169; PyArray_InnerProduct(op1: *mut PyObject, op2: *mut PyObject) -> *mut PyObject]; impl_api![170; PyArray_MatrixProduct(op1: *mut PyObject, op2: *mut PyObject) -> *mut PyObject]; - impl_api![171; NumPy1; PyArray_CopyAndTranspose(op: *mut PyObject) -> *mut PyObject]; + // Unused slot 171, was `PyArray_CopyAndTranspose` impl_api![172; PyArray_Correlate(op1: *mut PyObject, op2: *mut PyObject, mode: c_int) -> *mut PyObject]; - impl_api![173; NumPy1; PyArray_TypestrConvert(itemsize: c_int, gentype: c_int) -> c_int]; + // Unused slot 173, was `PyArray_TypestrConvert` impl_api![174; PyArray_DescrConverter(obj: *mut PyObject, at: *mut *mut PyArray_Descr) -> c_int]; impl_api![175; PyArray_DescrConverter2(obj: *mut PyObject, at: *mut *mut PyArray_Descr) -> c_int]; impl_api![176; PyArray_IntpConverter(obj: *mut PyObject, seq: *mut PyArray_Dims) -> c_int]; @@ -249,28 +303,28 @@ impl PyArrayAPI { impl_api![194; PyArray_RegisterCanCast(descr: *mut PyArray_Descr, totype: c_int, scalar: NPY_SCALARKIND) -> c_int]; impl_api![195; PyArray_InitArrFuncs(f: *mut PyArray_ArrFuncs)]; impl_api![196; PyArray_IntTupleFromIntp(len: c_int, vals: *mut npy_intp) -> *mut PyObject]; - impl_api![197; NumPy1; PyArray_ElementFromName(str: *mut c_char) -> c_int]; + // Unused slot 197, was `PyArray_TypeNumFromName` impl_api![198; PyArray_ClipmodeConverter(object: *mut PyObject, val: *mut NPY_CLIPMODE) -> c_int]; impl_api![199; PyArray_OutputConverter(object: *mut PyObject, address: *mut *mut PyArrayObject) -> c_int]; impl_api![200; PyArray_BroadcastToShape(obj: *mut PyObject, dims: *mut npy_intp, nd: c_int) -> *mut PyObject]; - impl_api![201; NumPy1; _PyArray_SigintHandler(signum: c_int)]; - impl_api![202; NumPy1; _PyArray_GetSigintBuf() -> *mut c_void]; + // Unused slot 201, was `_PyArray_SigintHandler` + // Unused slot 202, was `_PyArray_GetSigintBuf` impl_api![203; PyArray_DescrAlignConverter(obj: *mut PyObject, at: *mut *mut PyArray_Descr) -> c_int]; impl_api![204; PyArray_DescrAlignConverter2(obj: *mut PyObject, at: *mut *mut PyArray_Descr) -> c_int]; impl_api![205; PyArray_SearchsideConverter(obj: *mut PyObject, addr: *mut c_void) -> c_int]; impl_api![206; PyArray_CheckAxis(arr: *mut PyArrayObject, axis: *mut c_int, flags: c_int) -> *mut PyObject]; impl_api![207; PyArray_OverflowMultiplyList(l1: *mut npy_intp, n: c_int) -> npy_intp]; - impl_api![208; NumPy1; PyArray_CompareString(s1: *mut c_char, s2: *mut c_char, len: usize) -> c_int]; + // Unused slot 208, was `PyArray_CompareString` // impl_api![209; PyArray_MultiIterFromObjects(mps: *mut *mut PyObject, n: c_int, nadd: c_int, ...) -> *mut PyObject]; impl_api![210; PyArray_GetEndianness() -> c_int]; impl_api![211; PyArray_GetNDArrayCFeatureVersion() -> c_uint]; impl_api![212; PyArray_Correlate2(op1: *mut PyObject, op2: *mut PyObject, mode: c_int) -> *mut PyObject]; impl_api![213; PyArray_NeighborhoodIterNew(x: *mut PyArrayIterObject, bounds: *mut npy_intp, mode: c_int, fill: *mut PyArrayObject) -> *mut PyObject]; - impl_api![219; NumPy1; PyArray_SetDatetimeParseFunction(op: *mut PyObject)]; - impl_api![220; NumPy1; PyArray_DatetimeToDatetimeStruct(val: npy_datetime, fr: NPY_DATETIMEUNIT, result: *mut npy_datetimestruct)]; - impl_api![221; NumPy1; PyArray_TimedeltaToTimedeltaStruct(val: npy_timedelta, fr: NPY_DATETIMEUNIT, result: *mut npy_timedeltastruct)]; - impl_api![222; NumPy1; PyArray_DatetimeStructToDatetime(fr: NPY_DATETIMEUNIT, d: *mut npy_datetimestruct) -> npy_datetime]; - impl_api![223; NumPy1; PyArray_TimedeltaStructToTimedelta(fr: NPY_DATETIMEUNIT, d: *mut npy_timedeltastruct) -> npy_datetime]; + // Unused slot 219, was `PyArray_SetDatetimeParseFunction` + // Unused slot 220, was `PyArray_DatetimeToDatetimeStruct` + // Unused slot 221, was `PyArray_TimedeltaToTimedeltaStruct` + // Unused slot 222, was `PyArray_DatetimeStructToDatetime` + // Unused slot 223, was `PyArray_TimedeltaStructToTimedelta` impl_api![224; NpyIter_New(op: *mut PyArrayObject, flags: npy_uint32, order: NPY_ORDER, casting: NPY_CASTING, dtype: *mut PyArray_Descr) -> *mut NpyIter]; impl_api![225; NpyIter_MultiNew(nop: c_int, op_in: *mut *mut PyArrayObject, flags: npy_uint32, order: NPY_ORDER, casting: NPY_CASTING, op_flags: *mut npy_uint32, op_request_dtypes: *mut *mut PyArray_Descr) -> *mut NpyIter]; impl_api![226; NpyIter_AdvancedNew(nop: c_int, op_in: *mut *mut PyArrayObject, flags: npy_uint32, order: NPY_ORDER, casting: NPY_CASTING, op_flags: *mut npy_uint32, op_request_dtypes: *mut *mut PyArray_Descr, oa_ndim: c_int, op_axes: *mut *mut c_int, itershape: *mut npy_intp, buffersize: npy_intp) -> *mut NpyIter]; @@ -325,7 +379,7 @@ impl PyArrayAPI { impl_api![275; PyArray_CanCastTypeTo(from: *mut PyArray_Descr, to: *mut PyArray_Descr, casting: NPY_CASTING) -> npy_bool]; impl_api![276; PyArray_EinsteinSum(subscripts: *mut c_char, nop: npy_intp, op_in: *mut *mut PyArrayObject, dtype: *mut PyArray_Descr, order: NPY_ORDER, casting: NPY_CASTING, out: *mut PyArrayObject) -> *mut PyObject]; impl_api![277; PyArray_NewLikeArray(prototype: *mut PyArrayObject, order: NPY_ORDER, dtype: *mut PyArray_Descr, subok: c_int) -> *mut PyObject]; - impl_api![278; NumPy1; PyArray_GetArrayParamsFromObject(op: *mut PyObject, requested_dtype: *mut PyArray_Descr, writeable: npy_bool, out_dtype: *mut *mut PyArray_Descr, out_ndim: *mut c_int, out_dims: *mut npy_intp, out_arr: *mut *mut PyArrayObject, context: *mut PyObject) -> c_int]; + // Unused slot 278, was `PyArray_GetArrayParamsFromObject` impl_api![279; PyArray_ConvertClipmodeSequence(object: *mut PyObject, modes: *mut NPY_CLIPMODE, n: c_int) -> c_int]; impl_api![280; PyArray_MatrixProduct2(op1: *mut PyObject, op2: *mut PyObject, out: *mut PyArrayObject) -> *mut PyObject]; impl_api![281; NpyIter_IsFirstVisit(iter: *mut NpyIter, iop: c_int) -> npy_bool]; @@ -338,136 +392,50 @@ impl PyArrayAPI { impl_api![288; PyDataMem_NEW(size: usize) -> *mut c_void]; impl_api![289; PyDataMem_FREE(ptr: *mut c_void)]; impl_api![290; PyDataMem_RENEW(ptr: *mut c_void, size: usize) -> *mut c_void]; - impl_api![291; NumPy1; PyDataMem_SetEventHook(newhook: PyDataMem_EventHookFunc, user_data: *mut c_void, old_data: *mut *mut c_void) -> PyDataMem_EventHookFunc]; - impl_api![293; NumPy1; PyArray_MapIterSwapAxes(mit: *mut PyArrayMapIterObject, ret: *mut *mut PyArrayObject, getmap: c_int)]; - impl_api![294; NumPy1; PyArray_MapIterArray(a: *mut PyArrayObject, index: *mut PyObject) -> *mut PyObject]; - impl_api![295; NumPy1; PyArray_MapIterNext(mit: *mut PyArrayMapIterObject)]; + // Unused slot 291, was `PyDataMem_SetEventHook` + // Unused slot 293, was `PyArray_MapIterSwapAxes` + // Unused slot 294, was `PyArray_MapIterArray` + // Unused slot 295, was `PyArray_MapIterNext` impl_api![296; PyArray_Partition(op: *mut PyArrayObject, ktharray: *mut PyArrayObject, axis: c_int, which: NPY_SELECTKIND) -> c_int]; impl_api![297; PyArray_ArgPartition(op: *mut PyArrayObject, ktharray: *mut PyArrayObject, axis: c_int, which: NPY_SELECTKIND) -> *mut PyObject]; impl_api![298; PyArray_SelectkindConverter(obj: *mut PyObject, selectkind: *mut NPY_SELECTKIND) -> c_int]; impl_api![299; PyDataMem_NEW_ZEROED(size: usize, elsize: usize) -> *mut c_void]; impl_api![300; PyArray_CheckAnyScalarExact(obj: *mut PyObject) -> c_int]; - impl_api![301; NumPy1; PyArray_MapIterArrayCopyIfOverlap(a: *mut PyArrayObject, index: *mut PyObject, copy_if_overlap: c_int, extra_op: *mut PyArrayObject) -> *mut PyObject]; + // Unused slot 301, was `PyArray_MapIterArrayCopyIfOverlap` impl_api![302; PyArray_ResolveWritebackIfCopy(self_: *mut PyArrayObject) -> c_int]; impl_api![303; PyArray_SetWritebackIfCopyBase(arr: *mut PyArrayObject, base: *mut PyArrayObject) -> c_int]; - impl_api![304; PyDataMem_SetHandler(handler: *mut PyObject) -> *mut PyObject]; - impl_api![305; PyDataMem_GetHandler() -> *mut PyObject]; - impl_api![307; NumPy2; NpyDatetime_ConvertDatetime64ToDatetimeStruct(meta: *mut PyArray_DatetimeMetaData, dt: npy_datetime, out: *mut npy_datetimestruct) -> c_int]; - impl_api![308; NumPy2; NpyDatetime_ConvertDatetimeStructToDatetime64(meta: *mut PyArray_DatetimeMetaData, dts: *const npy_datetimestruct, out: *mut npy_datetime) -> c_int]; - impl_api![309; NumPy2; NpyDatetime_ConvertPyDateTimeToDatetimeStruct(obj: *mut PyObject, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, apply_tzinfo: c_int) -> c_int]; - impl_api![310; NumPy2; NpyDatetime_GetDatetimeISO8601StrLen(local: c_int, base: NPY_DATETIMEUNIT) -> c_int]; - impl_api![311; NumPy2; NpyDatetime_MakeISO8601Datetime(dts: *mut npy_datetimestruct, outstr: *mut c_char, outlen: npy_intp, local: c_int, utc: c_int, base: NPY_DATETIMEUNIT, tzoffset: c_int, casting: NPY_CASTING) -> c_int]; - impl_api![312; NumPy2; NpyDatetime_ParseISO8601Datetime(str: *const c_char, len: pyo3::ffi::Py_ssize_t, unit: NPY_DATETIMEUNIT, casting: NPY_CASTING, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, out_special: *mut npy_bool) -> c_int]; - impl_api![313; NumPy2; NpyString_load(allocator: *mut npy_string_allocator, packed_string: *const npy_packed_static_string, unpacked_string: *mut npy_static_string) -> c_int]; - impl_api![314; NumPy2; NpyString_pack(out: *mut npy_packed_static_string) -> c_int]; - impl_api![315; NumPy2; NpyString_pack_null(allocator: *mut npy_string_allocator, packed_string: *mut npy_packed_static_string) -> c_int]; - impl_api![316; NumPy2; NpyString_acquire_allocator(descr: *const PyArray_StringDTypeObject) -> *mut npy_string_allocator]; - impl_api![317; NumPy2; NpyString_acquire_allocators(n_descriptors: usize, descrs: *const *mut PyArray_Descr, allocators: *mut *mut npy_string_allocator)]; - impl_api![318; NumPy2; NpyString_release_allocator(allocator: *mut npy_string_allocator)]; - impl_api![319; NumPy2; NpyString_release_allocators(length: usize, allocators: *mut *mut npy_string_allocator)]; - impl_api![361; NumPy2; PyArray_GetDefaultDescr(DType: *mut PyArray_DTypeMeta) -> *mut PyArray_Descr]; - impl_api![362; NumPy2; PyArrayInitDTypeMeta_FromSpec(DType: *mut PyArray_DTypeMeta, spec: *mut PyArrayDTypeMeta_Spec) -> c_int]; - impl_api![363; NumPy2; PyArray_CommonDType(dtype1: *mut PyArray_DTypeMeta, dtype2: *mut PyArray_DTypeMeta) -> PyArray_DTypeMeta]; - impl_api![364; NumPy2; PyArray_PromoteDTypeSequence(length: npy_intp, dtypes_in: *mut *mut PyArray_DTypeMeta) -> *mut PyArray_DTypeMeta]; - impl_api![365; NumPy2; _PyDataType_GetArrFuncs(descr: *const PyArray_Descr) -> *mut PyArray_ArrFuncs]; - - #[allow(non_snake_case)] - pub unsafe fn PyArray_CopyInto<'py>( - &self, - py: Python<'py>, - dst: *mut PyArrayObject, - src: *mut PyArrayObject, - ) -> c_int { - let offset = if is_numpy_2(py) { 50 } else { 82 }; - let fptr = self.get(py, offset) - as *const extern "C" fn(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int; - (*fptr)(dst, src) - } - - #[allow(non_snake_case)] - pub unsafe fn PyArray_CastAnyTo<'py>( - &self, - py: Python<'py>, - out: *mut PyArrayObject, - mp: *mut PyArrayObject, - ) -> c_int { - let offset = if is_numpy_2(py) { 51 } else { 83 }; - let fptr = self.get(py, offset) - as *const extern "C" fn(out: *mut PyArrayObject, mp: *mut PyArrayObject) -> c_int; - (*fptr)(out, mp) - } -} - -// Define type objects associated with the NumPy API -macro_rules! impl_array_type { - ($(($offset:expr, $tname:ident)),*) => { - /// All type objects exported by the NumPy API. - #[allow(non_camel_case_types)] - pub enum NpyTypes { $($tname),* } - - impl PyArrayAPI { - /// Get a pointer of the type object associated with `ty`. - pub unsafe fn get_type_object<'py>(&self, py: Python<'py>, ty: NpyTypes) -> *mut PyTypeObject { - match ty { - $( NpyTypes::$tname => *(self.get(py, $offset)) as _ ),* - } - } - } - } -} - -impl_array_type! { - (1, PyBigArray_Type), - (2, PyArray_Type), - (3, PyArrayDescr_Type), - (4, PyArrayFlags_Type), - (5, PyArrayIter_Type), - (6, PyArrayMultiIter_Type), - (7, NPY_NUMUSERTYPES), - (8, PyBoolArrType_Type), - (9, _PyArrayScalar_BoolValues), - (10, PyGenericArrType_Type), - (11, PyNumberArrType_Type), - (12, PyIntegerArrType_Type), - (13, PySignedIntegerArrType_Type), - (14, PyUnsignedIntegerArrType_Type), - (15, PyInexactArrType_Type), - (16, PyFloatingArrType_Type), - (17, PyComplexFloatingArrType_Type), - (18, PyFlexibleArrType_Type), - (19, PyCharacterArrType_Type), - (20, PyByteArrType_Type), - (21, PyShortArrType_Type), - (22, PyIntArrType_Type), - (23, PyLongArrType_Type), - (24, PyLongLongArrType_Type), - (25, PyUByteArrType_Type), - (26, PyUShortArrType_Type), - (27, PyUIntArrType_Type), - (28, PyULongArrType_Type), - (29, PyULongLongArrType_Type), - (30, PyFloatArrType_Type), - (31, PyDoubleArrType_Type), - (32, PyLongDoubleArrType_Type), - (33, PyCFloatArrType_Type), - (34, PyCDoubleArrType_Type), - (35, PyCLongDoubleArrType_Type), - (36, PyObjectArrType_Type), - (37, PyStringArrType_Type), - (38, PyUnicodeArrType_Type), - (39, PyVoidArrType_Type) + // Min v1.22 impl_api![304; PyDataMem_SetHandler(handler: *mut PyObject) -> *mut PyObject]; + // Min v1.22 impl_api![305; PyDataMem_GetHandler() -> *mut PyObject]; + // Min v2.0 impl_api![307; NpyDatetime_ConvertDatetime64ToDatetimeStruct(meta: *mut PyArray_DatetimeMetaData, dt: npy_datetime, out: *mut npy_datetimestruct) -> c_int]; + // Min v2.0 impl_api![308; NpyDatetime_ConvertDatetimeStructToDatetime64(meta: *mut PyArray_DatetimeMetaData, dts: *const npy_datetimestruct, out: *mut npy_datetime) -> c_int]; + // Min v2.0 impl_api![309; NpyDatetime_ConvertPyDateTimeToDatetimeStruct(obj: *mut PyObject, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, apply_tzinfo: c_int) -> c_int]; + // Min v2.0 impl_api![310; NpyDatetime_GetDatetimeISO8601StrLen(local: c_int, base: NPY_DATETIMEUNIT) -> c_int]; + // Min v2.0 impl_api![311; NpyDatetime_MakeISO8601Datetime(dts: *mut npy_datetimestruct, outstr: *mut c_char, outlen: npy_intp, local: c_int, utc: c_int, base: NPY_DATETIMEUNIT, tzoffset: c_int, casting: NPY_CASTING) -> c_int]; + // Min v2.0 impl_api![312; NpyDatetime_ParseISO8601Datetime(str: *const c_char, len: pyo3::ffi::Py_ssize_t, unit: NPY_DATETIMEUNIT, casting: NPY_CASTING, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, out_special: *mut npy_bool) -> c_int]; + // Min v2.0 impl_api![313; NpyString_load(allocator: *mut npy_string_allocator, packed_string: *const npy_packed_static_string, unpacked_string: *mut npy_static_string) -> c_int]; + // Min v2.0 impl_api![314; NpyString_pack(out: *mut npy_packed_static_string) -> c_int]; + // Min v2.0 impl_api![315; NpyString_pack_null(allocator: *mut npy_string_allocator, packed_string: *mut npy_packed_static_string) -> c_int]; + // Min v2.0 impl_api![316; NpyString_acquire_allocator(descr: *const PyArray_StringDTypeObject) -> *mut npy_string_allocator]; + // Min v2.0 impl_api![317; NpyString_acquire_allocators(n_descriptors: usize, descrs: *const *mut PyArray_Descr, allocators: *mut *mut npy_string_allocator)]; + // Min v2.0 impl_api![318; NpyString_release_allocator(allocator: *mut npy_string_allocator)]; + // Min v2.0 impl_api![319; NpyString_release_allocators(length: usize, allocators: *mut *mut npy_string_allocator)]; + // Min v2.0 impl_api![361; PyArray_GetDefaultDescr(DType: *mut PyArray_DTypeMeta) -> *mut PyArray_Descr]; + // Min v2.0 impl_api![362; PyArrayInitDTypeMeta_FromSpec(DType: *mut PyArray_DTypeMeta, spec: *mut PyArrayDTypeMeta_Spec) -> c_int]; + // Min v2.0 impl_api![363; PyArray_CommonDType(dtype1: *mut PyArray_DTypeMeta, dtype2: *mut PyArray_DTypeMeta) -> PyArray_DTypeMeta]; + // Min v2.0 impl_api![364; PyArray_PromoteDTypeSequence(length: npy_intp, dtypes_in: *mut *mut PyArray_DTypeMeta) -> *mut PyArray_DTypeMeta]; + // Min v2.0 impl_api![365; _PyDataType_GetArrFuncs(descr: *const PyArray_Descr) -> *mut PyArray_ArrFuncs]; } /// Checks that `op` is an instance of `PyArray` or not. #[allow(non_snake_case)] pub unsafe fn PyArray_Check<'py>(py: Python<'py>, op: *mut PyObject) -> c_int { - ffi::PyObject_TypeCheck(op, PY_ARRAY_API.get_type_object(py, NpyTypes::PyArray_Type)) + ffi::PyObject_TypeCheck(op, super::get_type_object(py, NpyTypes::PyArray_Type)) } /// Checks that `op` is an exact instance of `PyArray` or not. #[allow(non_snake_case)] pub unsafe fn PyArray_CheckExact<'py>(py: Python<'py>, op: *mut PyObject) -> c_int { - (ffi::Py_TYPE(op) == PY_ARRAY_API.get_type_object(py, NpyTypes::PyArray_Type)) as _ + (ffi::Py_TYPE(op) == super::get_type_object(py, NpyTypes::PyArray_Type)) as _ } #[cfg(test)] diff --git a/src/npyffi/flags.rs b/src/npyffi/flags.rs index f39d5cc11..05f06c6e7 100644 --- a/src/npyffi/flags.rs +++ b/src/npyffi/flags.rs @@ -1,4 +1,4 @@ -use super::npy_uint32; +use super::{npy_uint32, npy_uint64}; use std::os::raw::c_int; pub const NPY_ARRAY_C_CONTIGUOUS: c_int = 0x0001; @@ -11,8 +11,8 @@ pub const NPY_ARRAY_ELEMENTSTRIDES: c_int = 0x0080; pub const NPY_ARRAY_ALIGNED: c_int = 0x0100; pub const NPY_ARRAY_NOTSWAPPED: c_int = 0x0200; pub const NPY_ARRAY_WRITEABLE: c_int = 0x0400; -pub const NPY_ARRAY_UPDATEIFCOPY: c_int = 0x1000; pub const NPY_ARRAY_WRITEBACKIFCOPY: c_int = 0x2000; +pub const NPY_ARRAY_ENSURENOCOPY: c_int = 0x4000; pub const NPY_ARRAY_BEHAVED: c_int = NPY_ARRAY_ALIGNED | NPY_ARRAY_WRITEABLE; pub const NPY_ARRAY_BEHAVED_NS: c_int = NPY_ARRAY_BEHAVED | NPY_ARRAY_NOTSWAPPED; pub const NPY_ARRAY_CARRAY: c_int = NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_BEHAVED; @@ -22,13 +22,14 @@ pub const NPY_ARRAY_FARRAY_RO: c_int = NPY_ARRAY_F_CONTIGUOUS | NPY_ARRAY_ALIGNE pub const NPY_ARRAY_DEFAULT: c_int = NPY_ARRAY_CARRAY; pub const NPY_ARRAY_IN_ARRAY: c_int = NPY_ARRAY_CARRAY_RO; pub const NPY_ARRAY_OUT_ARRAY: c_int = NPY_ARRAY_CARRAY; -pub const NPY_ARRAY_INOUT_ARRAY: c_int = NPY_ARRAY_CARRAY | NPY_ARRAY_UPDATEIFCOPY; +pub const NPY_ARRAY_INOUT_ARRAY: c_int = NPY_ARRAY_CARRAY; pub const NPY_ARRAY_INOUT_ARRAY2: c_int = NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY; pub const NPY_ARRAY_IN_FARRAY: c_int = NPY_ARRAY_FARRAY_RO; pub const NPY_ARRAY_OUT_FARRAY: c_int = NPY_ARRAY_FARRAY; -pub const NPY_ARRAY_INOUT_FARRAY: c_int = NPY_ARRAY_FARRAY | NPY_ARRAY_UPDATEIFCOPY; +pub const NPY_ARRAY_INOUT_FARRAY: c_int = NPY_ARRAY_FARRAY; pub const NPY_ARRAY_INOUT_FARRAY2: c_int = NPY_ARRAY_FARRAY | NPY_ARRAY_WRITEBACKIFCOPY; -pub const NPY_ARRAY_UPDATE_ALL: c_int = NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS; +pub const NPY_ARRAY_UPDATE_ALL: c_int = + NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS | NPY_ARRAY_ALIGNED; pub const NPY_ITER_C_INDEX: npy_uint32 = 0x00000001; pub const NPY_ITER_F_INDEX: npy_uint32 = 0x00000002; @@ -63,19 +64,18 @@ pub const NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE: npy_uint32 = 0x40000000; pub const NPY_ITER_GLOBAL_FLAGS: npy_uint32 = 0x0000ffff; pub const NPY_ITER_PER_OP_FLAGS: npy_uint32 = 0xffff0000; -pub const NPY_ITEM_REFCOUNT: u64 = 0x01; -pub const NPY_ITEM_HASOBJECT: u64 = 0x01; -pub const NPY_LIST_PICKLE: u64 = 0x02; -pub const NPY_ITEM_IS_POINTER: u64 = 0x04; -pub const NPY_NEEDS_INIT: u64 = 0x08; -pub const NPY_NEEDS_PYAPI: u64 = 0x10; -pub const NPY_USE_GETITEM: u64 = 0x20; -pub const NPY_USE_SETITEM: u64 = 0x40; -#[allow(overflowing_literals)] -pub const NPY_ALIGNED_STRUCT: u64 = 0x80; -pub const NPY_FROM_FIELDS: u64 = +pub const NPY_ITEM_REFCOUNT: npy_uint64 = 0x01; +pub const NPY_ITEM_HASOBJECT: npy_uint64 = 0x01; +pub const NPY_LIST_PICKLE: npy_uint64 = 0x02; +pub const NPY_ITEM_IS_POINTER: npy_uint64 = 0x04; +pub const NPY_NEEDS_INIT: npy_uint64 = 0x08; +pub const NPY_NEEDS_PYAPI: npy_uint64 = 0x10; +pub const NPY_USE_GETITEM: npy_uint64 = 0x20; +pub const NPY_USE_SETITEM: npy_uint64 = 0x40; +pub const NPY_ALIGNED_STRUCT: npy_uint64 = 0x80; +pub const NPY_FROM_FIELDS: npy_uint64 = NPY_NEEDS_INIT | NPY_LIST_PICKLE | NPY_ITEM_REFCOUNT | NPY_NEEDS_PYAPI; -pub const NPY_OBJECT_DTYPE_FLAGS: u64 = NPY_LIST_PICKLE +pub const NPY_OBJECT_DTYPE_FLAGS: npy_uint64 = NPY_LIST_PICKLE | NPY_USE_GETITEM | NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT diff --git a/src/npyffi/mod.rs b/src/npyffi/mod.rs index 0895be856..bd1659301 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -11,36 +11,32 @@ use std::mem::forget; use std::os::raw::{c_uint, c_void}; +use std::ptr::NonNull; use pyo3::{ + ffi::PyTypeObject, sync::PyOnceLock, types::{PyAnyMethods, PyCapsule, PyCapsuleMethods, PyModule}, PyResult, Python, }; -pub const API_VERSION_2_0: c_uint = 0x00000012; - static API_VERSION: PyOnceLock = PyOnceLock::new(); fn get_numpy_api<'py>( py: Python<'py>, module: &str, capsule: &str, -) -> PyResult<*const *const c_void> { +) -> PyResult> { let module = PyModule::import(py, module)?; let capsule = module.getattr(capsule)?.cast_into::()?; - let api = capsule - .pointer_checked(None)? - .cast::<*const c_void>() - .as_ptr() - .cast_const(); + let api = capsule.pointer_checked(None)?; // Intentionally leak a reference to the capsule // so we can safely cache a pointer into its interior. forget(capsule); - Ok(api) + Ok(api.cast()) } /// Returns whether the runtime `numpy` version is 2.0 or greater. @@ -48,7 +44,7 @@ pub fn is_numpy_2<'py>(py: Python<'py>) -> bool { let api_version = *API_VERSION.get_or_init(py, || unsafe { PY_ARRAY_API.PyArray_GetNDArrayCFeatureVersion(py) }); - api_version >= API_VERSION_2_0 + api_version >= NPY_2_0_API_VERSION } // Implements wrappers for NumPy's Array and UFunc API @@ -57,52 +53,90 @@ macro_rules! impl_api { [$offset: expr; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { #[allow(non_snake_case)] pub unsafe fn $fname<'py>(&self, py: Python<'py>, $($arg : $t), *) $(-> $ret)* { - let fptr = self.get(py, $offset) as *const extern "C" fn ($($arg : $t), *) $(-> $ret)*; - (*fptr)($($arg), *) + let f: extern "C" fn ($($arg : $t), *) $(-> $ret)* = self.get(py, $offset).cast().read(); + f($($arg), *) } }; +} - // API with version constraints, checked at runtime - [$offset: expr; NumPy1; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { - #[allow(non_snake_case)] - pub unsafe fn $fname<'py>(&self, py: Python<'py>, $($arg : $t), *) $(-> $ret)* { - assert!( - !is_numpy_2(py), - "{} requires API < {:08X} (NumPy 1) but the runtime version is API {:08X}", - stringify!($fname), - API_VERSION_2_0, - *API_VERSION.get(py).expect("API_VERSION is initialized"), - ); - let fptr = self.get(py, $offset) as *const extern "C" fn ($($arg: $t), *) $(-> $ret)*; - (*fptr)($($arg), *) - } +// Define type objects associated with the NumPy API +macro_rules! impl_array_type { + ($(($api:ident [ $offset:expr ] , $tname:ident)),* $(,)?) => { + /// All type objects exported by the NumPy API. + #[allow(non_camel_case_types)] + pub enum NpyTypes { $($tname),* } - }; - [$offset: expr; NumPy2; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { - #[allow(non_snake_case)] - pub unsafe fn $fname<'py>(&self, py: Python<'py>, $($arg : $t), *) $(-> $ret)* { - assert!( - is_numpy_2(py), - "{} requires API {:08X} or greater (NumPy 2) but the runtime version is API {:08X}", - stringify!($fname), - API_VERSION_2_0, - *API_VERSION.get(py).expect("API_VERSION is initialized"), - ); - let fptr = self.get(py, $offset) as *const extern "C" fn ($($arg: $t), *) $(-> $ret)*; - (*fptr)($($arg), *) + /// Get a pointer of the type object associated with `ty`. + pub unsafe fn get_type_object<'py>(py: Python<'py>, ty: NpyTypes) -> *mut PyTypeObject { + match ty { + $( NpyTypes::$tname => $api.get(py, $offset).read() as _ ),* + } } + } +} - }; +impl_array_type! { + // Multiarray API + // Slot 1 was never meaningfully used by NumPy + (PY_ARRAY_API[2], PyArray_Type), + (PY_ARRAY_API[3], PyArrayDescr_Type), + // Unused slot 4, was `PyArrayFlags_Type` + (PY_ARRAY_API[5], PyArrayIter_Type), + (PY_ARRAY_API[6], PyArrayMultiIter_Type), + // (PY_ARRAY_API[7], NPY_NUMUSERTYPES) -> c_int, + (PY_ARRAY_API[8], PyBoolArrType_Type), + // (PY_ARRAY_API[9], _PyArrayScalar_BoolValues) -> *mut PyBoolScalarObject, + (PY_ARRAY_API[10], PyGenericArrType_Type), + (PY_ARRAY_API[11], PyNumberArrType_Type), + (PY_ARRAY_API[12], PyIntegerArrType_Type), + (PY_ARRAY_API[13], PySignedIntegerArrType_Type), + (PY_ARRAY_API[14], PyUnsignedIntegerArrType_Type), + (PY_ARRAY_API[15], PyInexactArrType_Type), + (PY_ARRAY_API[16], PyFloatingArrType_Type), + (PY_ARRAY_API[17], PyComplexFloatingArrType_Type), + (PY_ARRAY_API[18], PyFlexibleArrType_Type), + (PY_ARRAY_API[19], PyCharacterArrType_Type), + (PY_ARRAY_API[20], PyByteArrType_Type), + (PY_ARRAY_API[21], PyShortArrType_Type), + (PY_ARRAY_API[22], PyIntArrType_Type), + (PY_ARRAY_API[23], PyLongArrType_Type), + (PY_ARRAY_API[24], PyLongLongArrType_Type), + (PY_ARRAY_API[25], PyUByteArrType_Type), + (PY_ARRAY_API[26], PyUShortArrType_Type), + (PY_ARRAY_API[27], PyUIntArrType_Type), + (PY_ARRAY_API[28], PyULongArrType_Type), + (PY_ARRAY_API[29], PyULongLongArrType_Type), + (PY_ARRAY_API[30], PyFloatArrType_Type), + (PY_ARRAY_API[31], PyDoubleArrType_Type), + (PY_ARRAY_API[32], PyLongDoubleArrType_Type), + (PY_ARRAY_API[33], PyCFloatArrType_Type), + (PY_ARRAY_API[34], PyCDoubleArrType_Type), + (PY_ARRAY_API[35], PyCLongDoubleArrType_Type), + (PY_ARRAY_API[36], PyObjectArrType_Type), + (PY_ARRAY_API[37], PyStringArrType_Type), + (PY_ARRAY_API[38], PyUnicodeArrType_Type), + (PY_ARRAY_API[39], PyVoidArrType_Type), + (PY_ARRAY_API[214], PyTimeIntegerArrType_Type), + (PY_ARRAY_API[215], PyDatetimeArrType_Type), + (PY_ARRAY_API[216], PyTimedeltaArrType_Type), + (PY_ARRAY_API[217], PyHalfArrType_Type), + (PY_ARRAY_API[218], NpyIter_Type), + // UFunc API + (PY_UFUNC_API[0], PyUFunc_Type), } pub mod array; pub mod flags; +mod npy_common; +mod numpyconfig; pub mod objects; pub mod types; pub mod ufunc; pub use self::array::*; pub use self::flags::*; +pub use self::npy_common::*; +pub use self::numpyconfig::*; pub use self::objects::*; pub use self::types::*; pub use self::ufunc::*; diff --git a/src/npyffi/npy_common.rs b/src/npyffi/npy_common.rs new file mode 100644 index 000000000..cf52bb430 --- /dev/null +++ b/src/npyffi/npy_common.rs @@ -0,0 +1,8 @@ +use std::ffi::c_int; + +/// Unknown CPU endianness. +pub const NPY_CPU_UNKNOWN_ENDIAN: c_int = 0; +/// CPU is little-endian. +pub const NPY_CPU_LITTLE: c_int = 1; +/// CPU is big-endian. +pub const NPY_CPU_BIG: c_int = 2; diff --git a/src/npyffi/numpyconfig.rs b/src/npyffi/numpyconfig.rs new file mode 100644 index 000000000..0cb901a39 --- /dev/null +++ b/src/npyffi/numpyconfig.rs @@ -0,0 +1,18 @@ +// This file matches the numpyconfig.h header. + +use std::ffi::c_uint; + +/// The current target ABI version +const NPY_ABI_VERSION: c_uint = 0x02000000; + +/// The current target API version (v1.15) +const NPY_API_VERSION: c_uint = 0x0000000c; + +pub(super) const NPY_2_0_API_VERSION: c_uint = 0x00000012; + +/// The current version of the `ndarray` object (ABI version). +pub const NPY_VERSION: c_uint = NPY_ABI_VERSION; +/// The current version of C API. +pub const NPY_FEATURE_VERSION: c_uint = NPY_API_VERSION; +/// The string representation of current version C API. +pub const NPY_FEATURE_VERSION_STRING: &str = "1.15"; diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index 4f4f1aaaf..5d9e43b86 100644 --- a/src/npyffi/objects.rs +++ b/src/npyffi/objects.rs @@ -10,6 +10,9 @@ use std::os::raw::*; use super::types::*; use crate::npyffi::*; +pub const NPY_NTYPES_ABI_COMPATIBLE: usize = 21; +pub const NPY_MAXDIMS_LEGACY_ITERS: usize = 32; + #[repr(C)] pub struct PyArrayObject { pub ob_base: PyObject, @@ -192,7 +195,7 @@ pub struct PyArray_ArrayDescr { #[repr(C)] #[derive(Copy, Clone)] pub struct PyArray_ArrFuncs { - pub cast: [PyArray_VectorUnaryFunc; 21usize], + pub cast: [PyArray_VectorUnaryFunc; NPY_NTYPES_ABI_COMPATIBLE], pub getitem: PyArray_GetItemFunc, pub setitem: PyArray_SetItemFunc, pub copyswapn: PyArray_CopySwapNFunc, @@ -205,15 +208,15 @@ pub struct PyArray_ArrFuncs { pub nonzero: PyArray_NonzeroFunc, pub fill: PyArray_FillFunc, pub fillwithscalar: PyArray_FillWithScalarFunc, - pub sort: [PyArray_SortFunc; 3usize], - pub argsort: [PyArray_ArgSortFunc; 3usize], + pub sort: [PyArray_SortFunc; NPY_NSORTS], + pub argsort: [PyArray_ArgSortFunc; NPY_NSORTS], pub castdict: *mut PyObject, pub scalarkind: PyArray_ScalarKindFunc, pub cancastscalarkindto: *mut *mut c_int, pub cancastto: *mut c_int, - pub fastclip: PyArray_FastClipFunc, - pub fastputmask: PyArray_FastPutmaskFunc, - pub fasttake: PyArray_FastTakeFunc, + pub _unused1: *mut c_void, + pub _unused2: *mut c_void, + pub _unused3: *mut c_void, pub argmin: PyArray_ArgFunc, } @@ -263,53 +266,9 @@ pub type PyArray_SortFunc = Option c_int>; pub type PyArray_ArgSortFunc = Option c_int>; -pub type PyArray_PartitionFunc = Option< - unsafe extern "C" fn( - *mut c_void, - npy_intp, - npy_intp, - *mut npy_intp, - *mut npy_intp, - *mut c_void, - ) -> c_int, ->; -pub type PyArray_ArgPartitionFunc = Option< - unsafe extern "C" fn( - *mut c_void, - *mut npy_intp, - npy_intp, - npy_intp, - *mut npy_intp, - *mut npy_intp, - *mut c_void, - ) -> c_int, ->; pub type PyArray_FillWithScalarFunc = Option c_int>; pub type PyArray_ScalarKindFunc = Option c_int>; -pub type PyArray_FastClipFunc = - Option; -pub type PyArray_FastPutmaskFunc = - Option; -pub type PyArray_FastTakeFunc = Option< - unsafe extern "C" fn( - *mut c_void, - *mut c_void, - *mut npy_intp, - npy_intp, - npy_intp, - npy_intp, - npy_intp, - NPY_CLIPMODE, - ) -> c_int, ->; - -#[repr(C)] -pub struct PyArrayFlagsObject { - pub ob_base: PyObject, - pub arr: *mut PyObject, - pub flags: c_int, -} #[repr(C)] #[derive(Clone, Copy)] @@ -365,9 +324,12 @@ pub struct PyUFuncObject { pub core_offsets: *mut c_int, pub core_signature: *mut c_char, pub type_resolver: PyUFunc_TypeResolutionFunc, - pub legacy_inner_loop_selector: PyUFunc_LegacyInnerLoopSelectionFunc, - pub reserved2: *mut c_void, - pub masked_inner_loop_selector: PyUFunc_MaskedInnerLoopSelectionFunc, + pub reserved2: *mut c_void, // Was the legacy loop resolver + #[cfg(all(Py_3_8, not(Py_LIMITED_API)))] + pub vectorcall: Option, + #[cfg(not(all(Py_3_8, not(Py_LIMITED_API))))] + pub vectorcall: *mut c_void, + pub reserved3: *mut c_void, // Was previously the `PyUFunc_MaskedInnerLoopSelectionFunc` pub op_flags: *mut npy_uint32, pub iter_flags: npy_uint32, } @@ -393,27 +355,6 @@ pub type PyUFunc_TypeResolutionFunc = Option< *mut *mut PyArray_Descr, ) -> c_int, >; -pub type PyUFunc_LegacyInnerLoopSelectionFunc = Option< - unsafe extern "C" fn( - *mut PyUFuncObject, - *mut *mut PyArray_Descr, - *mut PyUFuncGenericFunction, - *mut *mut c_void, - *mut c_int, - ) -> c_int, ->; -pub type PyUFunc_MaskedInnerLoopSelectionFunc = Option< - unsafe extern "C" fn( - *mut PyUFuncObject, - *mut *mut PyArray_Descr, - *mut PyArray_Descr, - *mut npy_intp, - npy_intp, - *mut PyUFunc_MaskedStridedInnerLoopFunc, - *mut *mut NpyAuxData, - *mut c_int, - ) -> c_int, ->; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -425,17 +366,17 @@ pub struct PyArrayIterObject { pub nd_m1: c_int, pub index: npy_intp, pub size: npy_intp, - pub coordinates: [npy_intp; 32usize], - pub dims_m1: [npy_intp; 32usize], - pub strides: [npy_intp; 32usize], - pub backstrides: [npy_intp; 32usize], - pub factors: [npy_intp; 32usize], + pub coordinates: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], + pub dims_m1: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], + pub strides: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], + pub backstrides: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], + pub factors: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], pub ao: *mut PyArrayObject, pub dataptr: *mut c_char, pub contiguous: npy_bool, - pub bounds: [[npy_intp; 2usize]; 32usize], - pub limits: [[npy_intp; 2usize]; 32usize], - pub limits_sizes: [npy_intp; 32usize], + pub bounds: [[npy_intp; 2usize]; NPY_MAXDIMS_LEGACY_ITERS], + pub limits: [[npy_intp; 2usize]; NPY_MAXDIMS_LEGACY_ITERS], + pub limits_sizes: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], pub translate: npy_iter_get_dataptr_t, } @@ -446,7 +387,7 @@ pub struct PyArrayMultiIterObject { pub size: npy_intp, pub index: npy_intp, pub nd: c_int, - pub dimensions: [npy_intp; 32usize], + pub dimensions: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], pub iters: [*mut PyArrayIterObject; 32usize], } @@ -456,62 +397,25 @@ pub struct PyArrayNeighborhoodIterObject { pub nd_m1: c_int, pub index: npy_intp, pub size: npy_intp, - pub coordinates: [npy_intp; 32usize], - pub dims_m1: [npy_intp; 32usize], - pub strides: [npy_intp; 32usize], - pub backstrides: [npy_intp; 32usize], - pub factors: [npy_intp; 32usize], + pub coordinates: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], + pub dims_m1: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], + pub strides: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], + pub backstrides: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], + pub factors: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], pub ao: *mut PyArrayObject, pub dataptr: *mut c_char, pub contiguous: npy_bool, - pub bounds: [[npy_intp; 2usize]; 32usize], - pub limits: [[npy_intp; 2usize]; 32usize], - pub limits_sizes: [npy_intp; 32usize], + pub bounds: [[npy_intp; 2usize]; NPY_MAXDIMS_LEGACY_ITERS], + pub limits: [[npy_intp; 2usize]; NPY_MAXDIMS_LEGACY_ITERS], + pub limits_sizes: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], pub translate: npy_iter_get_dataptr_t, pub nd: npy_intp, - pub dimensions: [npy_intp; 32usize], + pub dimensions: [npy_intp; NPY_MAXDIMS_LEGACY_ITERS], pub _internal_iter: *mut PyArrayIterObject, pub constant: *mut c_char, pub mode: c_int, } -#[repr(C)] -pub struct PyArrayMapIterObject { - pub ob_base: PyObject, - pub numiter: c_int, - pub size: npy_intp, - pub index: npy_intp, - pub nd: c_int, - pub dimensions: [npy_intp; 32usize], - pub outer: *mut NpyIter, - pub unused: [*mut c_void; 30usize], - pub array: *mut PyArrayObject, - pub ait: *mut PyArrayIterObject, - pub subspace: *mut PyArrayObject, - pub iteraxes: [c_int; 32usize], - pub fancy_strides: [npy_intp; 32usize], - pub baseoffset: *mut c_char, - pub consec: c_int, - pub dataptr: *mut c_char, - pub nd_fancy: c_int, - pub fancy_dims: [npy_intp; 32usize], - pub needs_api: c_int, - pub extra_op: *mut PyArrayObject, - pub extra_op_dtype: *mut PyArray_Descr, - pub extra_op_flags: *mut npy_uint32, - pub extra_op_iter: *mut NpyIter, - pub extra_op_next: NpyIter_IterNextFunc, - pub extra_op_ptrs: *mut *mut c_char, - pub outer_next: NpyIter_IterNextFunc, - pub outer_ptrs: *mut *mut c_char, - pub outer_strides: *mut npy_intp, - pub subspace_iter: *mut NpyIter, - pub subspace_next: NpyIter_IterNextFunc, - pub subspace_ptrs: *mut *mut c_char, - pub subspace_strides: *mut npy_intp, - pub iter_count: npy_intp, -} - pub type NpyIter_IterNextFunc = Option c_int>; pub type NpyIter_GetMultiIndexFunc = Option; pub type PyDataMem_EventHookFunc = @@ -547,8 +451,11 @@ pub struct PyArray_DatetimeDTypeMetaData { // npy_packed_static_string and npy_string_allocator are opaque pointers. // FIXME(adamreichold): Consider extern types when they are stabilized. // https://github.com/rust-lang/rust/issues/43467 -pub type npy_packed_static_string = c_void; -pub type npy_string_allocator = c_void; +#[repr(C)] +pub struct npy_packed_static_string([u8; 0]); + +#[repr(C)] +pub struct npy_string_allocator([u8; 0]); #[cfg(not(Py_LIMITED_API))] #[repr(C)] diff --git a/src/npyffi/types.rs b/src/npyffi/types.rs index eb03b801b..bb386e29c 100644 --- a/src/npyffi/types.rs +++ b/src/npyffi/types.rs @@ -1,12 +1,13 @@ -use pyo3::ffi::{Py_hash_t, Py_intptr_t, Py_uintptr_t}; -use std::os::raw::*; +use pyo3::ffi::{Py_hash_t, Py_ssize_t}; +use std::{ffi::c_float, os::raw::*}; -pub type npy_intp = Py_intptr_t; -pub type npy_uintp = Py_uintptr_t; +pub type npy_intp = Py_ssize_t; +pub type npy_uintp = usize; pub type npy_longlong = c_longlong; pub type npy_ulonglong = c_ulonglong; pub type npy_bool = c_uchar; -pub type npy_longdouble = f64; +// On some platforms long double is 80-bit extended precision +// pub type npy_longdouble = f64; pub type npy_byte = c_char; pub type npy_ubyte = c_uchar; pub type npy_ushort = c_ushort; @@ -16,8 +17,8 @@ pub type npy_char = c_char; pub type npy_short = c_short; pub type npy_int = c_int; pub type npy_long = c_long; -pub type npy_float = f32; -pub type npy_double = f64; +pub type npy_float = c_float; +pub type npy_double = c_double; pub type npy_hash_t = Py_hash_t; pub type npy_int64 = i64; pub type npy_uint64 = u64; @@ -34,30 +35,30 @@ pub type npy_float32 = f32; pub type npy_complex64 = npy_cfloat; pub type npy_half = npy_uint16; pub type npy_float16 = npy_half; -pub type npy_float128 = npy_longdouble; -pub type npy_complex256 = npy_clongdouble; +// pub type npy_float128 = npy_longdouble; +// pub type npy_complex256 = npy_clongdouble; pub type npy_timedelta = npy_int64; pub type npy_datetime = npy_int64; #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct npy_cdouble { - pub real: f64, - pub imag: f64, + pub real: c_double, + pub imag: c_double, } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct npy_cfloat { - pub real: f32, - pub imag: f32, + pub real: c_float, + pub imag: c_float, } -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct npy_clongdouble { - pub real: npy_longdouble, - pub imag: npy_longdouble, -} +// #[repr(C)] +// #[derive(Debug, Clone, Copy)] +// pub struct npy_clongdouble { +// pub real: npy_longdouble, +// pub imag: npy_longdouble, +// } #[repr(i32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -88,6 +89,8 @@ pub enum NPY_SORTKIND { NPY_MERGESORT = 2, } +pub const NPY_NSORTS: usize = NPY_SORTKIND::NPY_MERGESORT as usize + 1; + #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum NPY_SEARCHSIDE { @@ -226,14 +229,13 @@ pub enum NPY_TYPECHAR { NPY_CLONGDOUBLELTR = b'G', NPY_OBJECTLTR = b'O', NPY_STRINGLTR = b'S', - NPY_STRINGLTR2 = b'a', + NPY_DEPRECATED_STRINGLTR2 = b'a', NPY_UNICODELTR = b'U', NPY_VOIDLTR = b'V', NPY_DATETIMELTR = b'M', NPY_TIMEDELTALTR = b'm', NPY_CHARLTR = b'c', - NPY_INTPLTR = b'p', - NPY_UINTPLTR = b'P', + NPY_VSTRINGLTR = b'T', } // Note: NPY_TYPEKINDCHAR doesn't exist in the header and has been created here artificially @@ -280,5 +282,6 @@ pub enum NPY_ARRAYMETHOD_FLAGS { NPY_METH_SUPPORTS_UNALIGNED = 1 << 2, NPY_METH_IS_REORDERABLE = 1 << 3, _NPY_METH_FORCE_CAST_INPUTS = 1 << 17, - NPY_METH_RUNTIME_FLAGS = (1 << 0) | (1 << 1), // NPY_METH_REQUIRES_PYAPI | NPY_METH_NO_FLOATINGPOINT_ERRORS + NPY_METH_RUNTIME_FLAGS = + Self::NPY_METH_REQUIRES_PYAPI as i32 | Self::NPY_METH_NO_FLOATINGPOINT_ERRORS as i32, } diff --git a/src/npyffi/ufunc.rs b/src/npyffi/ufunc.rs index bc5d504ca..ba381de8c 100644 --- a/src/npyffi/ufunc.rs +++ b/src/npyffi/ufunc.rs @@ -22,14 +22,14 @@ const CAPSULE_NAME: &str = "_UFUNC_API"; /// pointer to [Numpy UFunc API](https://numpy.org/doc/stable/reference/c-api/ufunc.html). pub static PY_UFUNC_API: PyUFuncAPI = PyUFuncAPI(PyOnceLock::new()); -pub struct PyUFuncAPI(PyOnceLock<*const *const c_void>); +pub struct PyUFuncAPI(PyOnceLock>); unsafe impl Send for PyUFuncAPI {} unsafe impl Sync for PyUFuncAPI {} impl PyUFuncAPI { - unsafe fn get<'py>(&self, py: Python<'py>, offset: isize) -> *const *const c_void { + pub(super) unsafe fn get<'py>(&self, py: Python<'py>, offset: isize) -> NonNull<*const c_void> { let api = self .0 .get_or_try_init(py, || get_numpy_api(py, mod_name(py)?, CAPSULE_NAME)) @@ -42,7 +42,7 @@ impl PyUFuncAPI { impl PyUFuncAPI { impl_api![1; PyUFunc_FromFuncAndData(func: *mut PyUFuncGenericFunction, data: *mut *mut c_void, types: *mut c_char, ntypes: c_int, nin: c_int, nout: c_int, identity: c_int, name: *const c_char, doc: *const c_char, unused: c_int) -> *mut PyObject]; impl_api![2; PyUFunc_RegisterLoopForType(ufunc: *mut PyUFuncObject, usertype: c_int, function: PyUFuncGenericFunction, arg_types: *mut c_int, data: *mut c_void) -> c_int]; - impl_api![3; PyUFunc_GenericFunction(ufunc: *mut PyUFuncObject, args: *mut PyObject, kwds: *mut PyObject, op: *mut *mut PyArrayObject) -> c_int]; + // Unused slot 3, was `PyUFunc_GenericFunction` impl_api![4; PyUFunc_f_f_As_d_d(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)]; impl_api![5; PyUFunc_d_d(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)]; impl_api![6; PyUFunc_f_f(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)]; @@ -64,11 +64,11 @@ impl PyUFuncAPI { impl_api![22; PyUFunc_O_O_method(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)]; impl_api![23; PyUFunc_OO_O_method(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)]; impl_api![24; PyUFunc_On_Om(args: *mut *mut c_char, dimensions: *mut npy_intp, steps: *mut npy_intp, func: *mut c_void)]; - impl_api![25; PyUFunc_GetPyValues(name: *mut c_char, bufsize: *mut c_int, errmask: *mut c_int, errobj: *mut *mut PyObject) -> c_int]; - impl_api![26; PyUFunc_checkfperr(errmask: c_int, errobj: *mut PyObject, first: *mut c_int) -> c_int]; + // Unused slot 25, was `PyUFunc_GetPyValues` + // Unused slot 26, was `PyUFunc_checkfperr` impl_api![27; PyUFunc_clearfperr()]; impl_api![28; PyUFunc_getfperr() -> c_int]; - impl_api![29; PyUFunc_handlefperr(errmask: c_int, errobj: *mut PyObject, retstatus: c_int, first: *mut c_int) -> c_int]; + // Unused slot 29, was `PyUFunc_handlefperr` impl_api![30; PyUFunc_ReplaceLoopBySignature(func: *mut PyUFuncObject, newfunc: PyUFuncGenericFunction, signature: *mut c_int, oldfunc: *mut PyUFuncGenericFunction) -> c_int]; impl_api![31; PyUFunc_FromFuncAndDataAndSignature(func: *mut PyUFuncGenericFunction, data: *mut *mut c_void, types: *mut c_char, ntypes: c_int, nin: c_int, nout: c_int, identity: c_int, name: *const c_char, doc: *const c_char, unused: c_int, signature: *const c_char) -> *mut PyObject]; impl_api![32; PyUFunc_SetUsesArraysAsData(data: *mut *mut c_void, i: usize) -> c_int]; @@ -81,18 +81,27 @@ impl PyUFuncAPI { impl_api![39; PyUFunc_DefaultTypeResolver(ufunc: *mut PyUFuncObject, casting: NPY_CASTING, operands: *mut *mut PyArrayObject, type_tup: *mut PyObject, out_dtypes: *mut *mut PyArray_Descr) -> c_int]; impl_api![40; PyUFunc_ValidateCasting(ufunc: *mut PyUFuncObject, casting: NPY_CASTING, operands: *mut *mut PyArrayObject, dtypes: *mut *mut PyArray_Descr) -> c_int]; impl_api![41; PyUFunc_RegisterLoopForDescr(ufunc: *mut PyUFuncObject, user_dtype: *mut PyArray_Descr, function: PyUFuncGenericFunction, arg_dtypes: *mut *mut PyArray_Descr, data: *mut c_void) -> c_int]; - impl_api![42; PyUFunc_FromFuncAndDataAndSignatureAndIdentity( - ufunc: *mut PyUFuncObject, - data: *mut *mut c_void, - types: *mut c_char, - ntypes: c_int, - nin: c_int, - nout: c_int, - identity: c_int, - name: *const c_char, - doc: *const c_char, - unused: c_int, - signature: *const c_char, - identity_value: *const c_char, - ) -> c_int]; + // Min v1.16 impl_api![42; PyUFunc_FromFuncAndDataAndSignatureAndIdentity( + // ufunc: *mut PyUFuncObject, + // data: *mut *mut c_void, + // types: *mut c_char, + // ntypes: c_int, + // nin: c_int, + // nout: c_int, + // identity: c_int, + // name: *const c_char, + // doc: *const c_char, + // unused: c_int, + // signature: *const c_char, + // identity_value: *const c_char, + // ) -> c_int]; + // Min v2.0 impl_api![43; PyUFunc_AddLoopFromSpec(ufunc: *mut PyObject, spec: *mut PyArrayMethod_Spec) -> c_int]; + // Min v2.0 impl_api![44; PyUFunc_AddPromoter(ufunc: *mut PyObject, dtype_tuple: *mut PyObject, promoter: *mut PyObject) -> c_int]; + // Min v2.0 impl_api![45; PyUFunc_AddWrappingLoop( + // ufunc_obj: *mut PyObject, + // new_dtypes: *mut *mut PyArray_DTypeMeta, + // wrapped_dtypes: *mut *mut PyArray_DTypeMeta, + // translate_given_descrs: *mut PyArrayMethod_TranslateGivenDescriptors, + // translate_loop_descrs: *mut PyArrayMethod_TranslateLoopDescriptors) -> c_int]; + // Min v2.0 impl_api![46; PyUFunc_GiveFloatingpointErrors(name: *mut c_char, fpe_errors: c_int) -> c_int]; } diff --git a/src/untyped_array.rs b/src/untyped_array.rs index e49991ea8..4b5020009 100644 --- a/src/untyped_array.rs +++ b/src/untyped_array.rs @@ -64,7 +64,7 @@ unsafe impl PyTypeInfo for PyUntypedArray { const MODULE: Option<&'static str> = Some("numpy"); fn type_object_raw<'py>(py: Python<'py>) -> *mut ffi::PyTypeObject { - unsafe { npyffi::PY_ARRAY_API.get_type_object(py, npyffi::NpyTypes::PyArray_Type) } + unsafe { npyffi::get_type_object(py, npyffi::NpyTypes::PyArray_Type) } } fn is_type_of(ob: &Bound<'_, PyAny>) -> bool {