From c78c5ffa3e56bf6e6d4dbe0027eb0b3c80f13b85 Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Tue, 4 Jun 2024 09:53:53 +1000 Subject: [PATCH 01/11] Add feature flags for numpy 1 and 2 By default, the extension is compile with support for numpy 1 and 2 (with runtime checks to pick the right binary offset where needed). Features or fields that are specific to a version are hidden by default. Users can opt-out of numpy 1 + numpy 2 by disabling default features and selecting a version. The library panics if the runtime version does not match the compilation version if only one version is selected. --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 4c71108a..0bf5f917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,8 @@ nalgebra = { version = "0.32", default-features = false, features = ["std"] } [package.metadata.docs.rs] all-features = true + +[features] +default = ["numpy-1", "numpy-2"] +numpy-1 = [] +numpy-2 = [] From fb7d512701b8605922367161a8dc8fec06c4b026 Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Tue, 4 Jun 2024 09:57:18 +1000 Subject: [PATCH 02/11] Remove outdated 1.0 functions, add new 2.0 functions Essentially a port of https://github.com/numpy/numpy/blob/97356bc6f0d6538389a9eef475d883a0f4024c2a/numpy/_core/include/numpy/npy_2_compat.h --- src/npyffi/array.rs | 120 +++++++++++++++++- src/npyffi/flags.rs | 29 +++-- src/npyffi/mod.rs | 38 ++++++ src/npyffi/objects.rs | 287 ++++++++++++++++++++++++++++++++++++++++++ src/npyffi/types.rs | 17 +++ 5 files changed, 478 insertions(+), 13 deletions(-) diff --git a/src/npyffi/array.rs b/src/npyffi/array.rs index b62c3b22..224b724f 100644 --- a/src/npyffi/array.rs +++ b/src/npyffi/array.rs @@ -68,8 +68,14 @@ 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![50; PyArray_CastTo(out: *mut PyArrayObject, mp: *mut PyArrayObject) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![50; PyArray_CopyInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![51; PyArray_CastAnyTo(out: *mut PyArrayObject, mp: *mut PyArrayObject) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![51; PyArray_CopyAnyInto(dst: *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]; @@ -83,9 +89,15 @@ 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![65; PyArray_ScalarFromObject(object: *mut PyObject) -> *mut PyObject]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![65; PyArray_Pack(descr: *mut PyArray_Descr, item: *mut c_void, value: *const PyObject) -> *mut PyObject]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![66; PyArray_GetCastFunc(descr: *mut PyArray_Descr, type_num: c_int) -> PyArray_VectorUnaryFunc]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![67; PyArray_FromDims(nd: c_int, d: *mut c_int, type_: c_int) -> *mut PyObject]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![68; PyArray_FromDimsAndDataAndDescr(nd: c_int, d: *mut c_int, descr: *mut PyArray_Descr, data: *mut c_char) -> *mut PyObject]; 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]; @@ -99,8 +111,11 @@ 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![81; PyArray_MoveInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![82; PyArray_CopyInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![83; PyArray_CopyAnyInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; 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]; @@ -121,6 +136,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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![103; PyArray_FillObjectArray(arr: *mut PyArrayObject, obj: *mut PyObject)]; 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]; @@ -133,13 +149,16 @@ 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![115; PyArray_NewFlagsObject(obj: *mut PyObject) -> *mut PyObject]; impl_api![116; PyArray_CanCastScalar(from: *mut PyTypeObject, to: *mut PyTypeObject) -> npy_bool]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![117; PyArray_CompareUCS4(s1: *mut npy_ucs4, s2: *mut npy_ucs4, len: usize) -> c_int]; 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)]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![122; PyArray_FieldNames(fields: *mut PyObject) -> *mut PyObject]; 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]; @@ -181,7 +200,9 @@ 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![163; PyArray_As1D(op: *mut *mut PyObject, ptr: *mut *mut c_char, d1: *mut c_int, typecode: c_int) -> c_int]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![164; 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]; 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]; @@ -189,8 +210,10 @@ impl PyArrayAPI { 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![171; PyArray_CopyAndTranspose(op: *mut PyObject) -> *mut PyObject]; impl_api![172; PyArray_Correlate(op1: *mut PyObject, op2: *mut PyObject, mode: c_int) -> *mut PyObject]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![173; PyArray_TypestrConvert(itemsize: c_int, gentype: c_int) -> c_int]; 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]; @@ -210,32 +233,41 @@ impl PyArrayAPI { impl_api![189; PyArray_LexSort(sort_keys: *mut PyObject, axis: c_int) -> *mut PyObject]; impl_api![190; PyArray_Round(a: *mut PyArrayObject, decimals: c_int, out: *mut PyArrayObject) -> *mut PyObject]; impl_api![191; PyArray_EquivTypenums(typenum1: c_int, typenum2: c_int) -> c_uchar]; - impl_api![192; PyArray_RegisterDataType(descr: *mut PyArray_Descr) -> c_int]; + impl_api![192; PyArray_RegisterDataType(descr: *mut PyArray_DescrProto) -> c_int]; impl_api![193; PyArray_RegisterCastFunc(descr: *mut PyArray_Descr, totype: c_int, castfunc: PyArray_VectorUnaryFunc) -> c_int]; 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![197; PyArray_ElementFromName(str: *mut c_char) -> c_int]; 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![201; _PyArray_SigintHandler(signum: c_int)]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![202; _PyArray_GetSigintBuf() -> *mut c_void]; 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![208; PyArray_CompareString(s1: *mut c_char, s2: *mut c_char, len: usize) -> c_int]; // 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![219; PyArray_SetDatetimeParseFunction(op: *mut PyObject)]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![220; PyArray_DatetimeToDatetimeStruct(val: npy_datetime, fr: NPY_DATETIMEUNIT, result: *mut npy_datetimestruct)]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![221; PyArray_TimedeltaToTimedeltaStruct(val: npy_timedelta, fr: NPY_DATETIMEUNIT, result: *mut npy_timedeltastruct)]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![222; PyArray_DatetimeStructToDatetime(fr: NPY_DATETIMEUNIT, d: *mut npy_datetimestruct) -> npy_datetime]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![223; PyArray_TimedeltaStructToTimedelta(fr: NPY_DATETIMEUNIT, d: *mut npy_timedeltastruct) -> npy_datetime]; 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]; @@ -291,6 +323,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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![278; 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]; 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]; @@ -304,18 +337,103 @@ 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![291; PyDataMem_SetEventHook(newhook: PyDataMem_EventHookFunc, user_data: *mut c_void, old_data: *mut *mut c_void) -> PyDataMem_EventHookFunc]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![293; PyArray_MapIterSwapAxes(mit: *mut PyArrayMapIterObject, ret: *mut *mut PyArrayObject, getmap: c_int)]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![294; PyArray_MapIterArray(a: *mut PyArrayObject, index: *mut PyObject) -> *mut PyObject]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![295; PyArray_MapIterNext(mit: *mut PyArrayMapIterObject)]; 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]; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] impl_api![301; PyArray_MapIterArrayCopyIfOverlap(a: *mut PyArrayObject, index: *mut PyObject, copy_if_overlap: c_int, extra_op: *mut PyArrayObject) -> *mut PyObject]; 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]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![307; NpyDatetime_ConvertDatetime64ToDatetimeStruct(meta: *mut PyArray_DatetimeMetaData, dt: npy_datetime, out: *mut npy_datetimestruct) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![308; NpyDatetime_ConvertDatetimeStructToDatetime64(meta: *mut PyArray_DatetimeMetaData, dts: *const npy_datetimestruct, out: *mut npy_datetime) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![309; NpyDatetime_ConvertPyDateTimeToDatetimeStruct(obj: *mut PyObject, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, apply_tzinfo: c_int) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![310; NpyDatetime_GetDatetimeISO8601StrLen(local: c_int, base: NPY_DATETIMEUNIT) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + 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]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + 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]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + 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]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![314; NpyString_pack(out: *mut npy_packed_static_string) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![315; NpyString_pack_null(allocator: *mut npy_string_allocator, packed_string: *mut npy_packed_static_string) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![316; NpyString_acquire_allocator(descr: *const PyArray_StringDTypeObject) -> *mut npy_string_allocator]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![317; NpyString_acquire_allocators(n_descriptors: usize, descrs: *const *mut PyArray_Descr, allocators: *mut *mut npy_string_allocator)]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![318; NpyString_release_allocator(allocator: *mut npy_string_allocator)]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![319; NpyString_release_allocators(length: usize, allocators: *mut *mut npy_string_allocator)]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![361; PyArray_GetDefaultDescr(DType: *mut PyArray_DTypeMeta) -> *mut PyArray_Descr]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![362; PyArrayInitDTypeMeta_FromSpec(DType: *mut PyArray_DTypeMeta, spec: *mut PyArrayDTypeMeta_Spec) -> c_int]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![363; PyArray_CommonDType(dtype1: *mut PyArray_DTypeMeta, dtype2: *mut PyArray_DTypeMeta) -> PyArray_DTypeMeta]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![364; PyArray_PromoteDTypeSequence(length: npy_intp, dtypes_in: *mut *mut PyArray_DTypeMeta) -> *mut PyArray_DTypeMeta]; + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + impl_api![365; _PyDataType_GetArrFuncs(descr: *const PyArray_Descr) -> *mut PyArray_ArrFuncs]; + + #[cfg(all(feature = "numpy-1", feature = "numpy-2"))] + #[allow(non_snake_case)] + pub unsafe fn PyArray_CopyInto<'py>( + &self, + py: Python<'py>, + dst: *mut PyArrayObject, + src: *mut PyArrayObject, + ) -> c_int { + let (_, api_version) = *ABI_API_VERSIONS + .get() + .expect("ABI_API_VERSIONS is initialized"); + let offset = if api_version < NPY_2_0_API_VERSION { + 82 + } else { + 50 + }; + let fptr = self.get(py, offset) + as *const extern "C" fn(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int; + (*fptr)(dst, src) + } + + #[cfg(all(feature = "numpy-1", feature = "numpy-2"))] + #[allow(non_snake_case)] + pub unsafe fn PyArray_CastAnyTo<'py>( + &self, + py: Python<'py>, + out: *mut PyArrayObject, + mp: *mut PyArrayObject, + ) -> c_int { + let (_, api_version) = *ABI_API_VERSIONS + .get() + .expect("ABI_API_VERSIONS is initialized"); + let offset = if api_version < NPY_2_0_API_VERSION { + 83 + } else { + 51 + }; + 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 diff --git a/src/npyffi/flags.rs b/src/npyffi/flags.rs index 7c9dedb6..ba61d152 100644 --- a/src/npyffi/flags.rs +++ b/src/npyffi/flags.rs @@ -1,4 +1,4 @@ -use super::{npy_char, npy_uint32}; +use super::npy_uint32; use std::os::raw::c_int; pub const NPY_ARRAY_C_CONTIGUOUS: c_int = 0x0001; @@ -63,19 +63,24 @@ 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: npy_char = 0x01; -pub const NPY_ITEM_HASOBJECT: npy_char = 0x01; -pub const NPY_LIST_PICKLE: npy_char = 0x02; -pub const NPY_ITEM_IS_POINTER: npy_char = 0x04; -pub const NPY_NEEDS_INIT: npy_char = 0x08; -pub const NPY_NEEDS_PYAPI: npy_char = 0x10; -pub const NPY_USE_GETITEM: npy_char = 0x20; -pub const NPY_USE_SETITEM: npy_char = 0x40; +#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] +pub use super::npy_char as FlagType; +#[cfg(feature = "numpy-2")] +pub use u64 as FlagType; + +pub const NPY_ITEM_REFCOUNT: FlagType = 0x01; +pub const NPY_ITEM_HASOBJECT: FlagType = 0x01; +pub const NPY_LIST_PICKLE: FlagType = 0x02; +pub const NPY_ITEM_IS_POINTER: FlagType = 0x04; +pub const NPY_NEEDS_INIT: FlagType = 0x08; +pub const NPY_NEEDS_PYAPI: FlagType = 0x10; +pub const NPY_USE_GETITEM: FlagType = 0x20; +pub const NPY_USE_SETITEM: FlagType = 0x40; #[allow(overflowing_literals)] -pub const NPY_ALIGNED_STRUCT: npy_char = 0x80; -pub const NPY_FROM_FIELDS: npy_char = +pub const NPY_ALIGNED_STRUCT: FlagType = 0x80; +pub const NPY_FROM_FIELDS: FlagType = NPY_NEEDS_INIT | NPY_LIST_PICKLE | NPY_ITEM_REFCOUNT | NPY_NEEDS_PYAPI; -pub const NPY_OBJECT_DTYPE_FLAGS: npy_char = NPY_LIST_PICKLE +pub const NPY_OBJECT_DTYPE_FLAGS: FlagType = 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 1f71f2ca..56a76bad 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -10,6 +10,7 @@ )] use std::mem::forget; +use std::os::raw::c_uint; use std::os::raw::c_void; use pyo3::{ @@ -17,6 +18,13 @@ use pyo3::{ PyResult, Python, }; +#[cfg(not(any(feature = "numpy-1", feature = "numpy-2")))] +compile_error!("at least one of feature \"numpy-1\" and feature \"numpy-2\" must be enabled"); + +pub const NPY_2_0_API_VERSION: c_uint = 0x00000012; + +pub static ABI_API_VERSIONS: std::sync::OnceLock<(c_uint, c_uint)> = std::sync::OnceLock::new(); + fn get_numpy_api<'py>( py: Python<'py>, module: &str, @@ -31,6 +39,36 @@ fn get_numpy_api<'py>( // so we can safely cache a pointer into its interior. forget(capsule); + ABI_API_VERSIONS.get_or_init(|| { + let abi_version = unsafe { + #[allow(non_snake_case)] + let PyArray_GetNDArrayCVersion = api.offset(0) as *const extern fn () -> c_uint; + (*PyArray_GetNDArrayCVersion)() + }; + let api_version = unsafe { + #[allow(non_snake_case)] + let PyArray_GetNDArrayCFeatureVersion = api.offset(211) as *const extern fn () -> c_uint; + (*PyArray_GetNDArrayCFeatureVersion)() + }; + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] + if api_version < NPY_2_0_API_VERSION { + panic!( + "the extension was compiled for numpy 1.x but the runtime version is 2.x (ABI {:08x}.{:08x})", + abi_version, + api_version + ); + } + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + if api_version >= NPY_2_0_API_VERSION { + panic!( + "the extension was compiled for numpy 2.x but the runtime version is 1.x (ABI {:08x}.{:08x})", + abi_version, + api_version + ); + } + (abi_version, api_version) + }); + Ok(api) } diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index 6b252bcb..15b95a5c 100644 --- a/src/npyffi/objects.rs +++ b/src/npyffi/objects.rs @@ -9,6 +9,9 @@ use std::os::raw::*; use super::types::*; +#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] +use crate::npyffi::*; + #[repr(C)] #[derive(Copy, Clone)] pub struct PyArrayObject { @@ -23,9 +26,22 @@ pub struct PyArrayObject { pub weakreflist: *mut PyObject, } +#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] #[repr(C)] #[derive(Copy, Clone)] pub struct PyArray_Descr { + pub ob_base: PyObject, + pub typeobj: *mut PyTypeObject, + pub kind: c_char, + pub type_: c_char, + pub byteorder: c_char, + pub _former_flags: c_char, + pub type_num: c_int, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct PyArray_DescrProto { pub ob_base: PyObject, pub typeobj: *mut PyTypeObject, pub kind: c_char, @@ -44,6 +60,216 @@ pub struct PyArray_Descr { pub hash: npy_hash_t, } +#[repr(C)] +#[derive(Copy, Clone)] +pub struct _PyArray_DescrNumPy2 { + pub ob_base: PyObject, + pub typeobj: *mut PyTypeObject, + pub kind: c_char, + pub type_: c_char, + pub byteorder: c_char, + pub _former_flags: c_char, + pub type_num: c_int, + pub flags: npy_uint64, + pub elsize: npy_intp, + pub alignment: npy_intp, + pub metadata: *mut PyObject, + pub hash: npy_hash_t, + pub reserved_null: [*mut std::ffi::c_void; 2], +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct _PyArray_LegacyDescr { + pub ob_base: PyObject, + pub typeobj: *mut PyTypeObject, + pub kind: c_char, + pub type_: c_char, + pub byteorder: c_char, + pub _former_flags: c_char, + pub type_num: c_int, + pub flags: npy_uint64, + pub elsize: npy_intp, + pub alignment: npy_intp, + pub metadata: *mut PyObject, + pub hash: npy_hash_t, + pub reserved_null: [*mut std::ffi::c_void; 2], + pub subarray: *mut PyArray_ArrayDescr, + pub fields: *mut PyObject, + pub names: *mut PyObject, + pub c_metadata: *mut NpyAuxData, +} + +#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] +pub use PyArray_DescrProto as PyArray_Descr; + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +pub use _PyArray_DescrNumPy2 as PyArray_Descr; + +#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] +#[allow(non_snake_case)] +#[inline(always)] +pub fn PyDataType_ISLEGACY(_dtype: *const PyArray_Descr) -> bool { + true +} + +#[cfg(feature = "numpy-2")] +#[allow(non_snake_case)] +#[inline(always)] +pub fn PyDataType_ISLEGACY(dtype: *const PyArray_Descr) -> bool { + unsafe { (*dtype).type_num < NPY_TYPES::NPY_VSTRING as i32 && (*dtype).type_num >= 0 } +} + +#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] +#[allow(non_snake_case)] +#[inline(always)] +pub fn PyDataType_SET_ELSIZE(dtype: *mut PyArray_Descr, size: npy_intp) { + unsafe { + (*dtype).elsize = size as c_int; + } +} + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +#[allow(non_snake_case)] +#[inline(always)] +pub fn PyDataType_SET_ELSIZE(dtype: *mut PyArray_Descr, size: npy_intp) { + unsafe { + (*dtype).elsize = size; + } +} + +#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] +#[allow(non_snake_case)] +#[inline(always)] +pub fn PyDataType_SET_ELSIZE(dtype: *mut PyArray_Descr, size: npy_intp) { + let (_, api_version) = *ABI_API_VERSIONS + .get() + .expect("ABI_API_VERSIONS is initialized"); + if api_version < NPY_2_0_API_VERSION { + unsafe { + (*(dtype as *mut PyArray_DescrProto)).elsize = size as c_int; + } + } else { + unsafe { + (*(dtype as *mut _PyArray_DescrNumPy2)).elsize = size; + } + } +} + +#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] +#[allow(non_snake_case)] +#[inline(always)] +pub fn PyDataType_FLAGS(dtype: *const PyArray_Descr) -> npy_uint64 { + unsafe { (*dtype).flags as c_uchar as npy_uint64 } +} + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +#[allow(non_snake_case)] +#[inline(always)] +pub fn PyDataType_FLAGS(dtype: *const PyArray_Descr) -> npy_uint64 { + unsafe { (*dtype).flags } +} + +#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] +#[allow(non_snake_case)] +#[inline(always)] +pub fn PyDataType_FLAGS(dtype: *const PyArray_Descr) -> npy_uint64 { + let (_, api_version) = *ABI_API_VERSIONS + .get() + .expect("ABI_API_VERSIONS is initialized"); + if api_version < NPY_2_0_API_VERSION { + unsafe { (*(dtype as *mut PyArray_DescrProto)).flags as c_uchar as npy_uint64 } + } else { + unsafe { (*(dtype as *mut _PyArray_DescrNumPy2)).flags } + } +} + +#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] +macro_rules! DESCR_ACCESSOR { + ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $zero:expr) => { + #[allow(non_snake_case)] + #[inline(always)] + pub fn $name(dtype: *const PyArray_Descr) -> $type { + unsafe { (*dtype).$property as $type } + } + }; +} + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +macro_rules! DESCR_ACCESSOR { + ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $zero:expr) => { + #[allow(non_snake_case)] + #[inline(always)] + pub fn $name(dtype: *const PyArray_Descr) -> $type { + if $legacy_only && !PyDataType_ISLEGACY(dtype) { + $zero + } else { + unsafe { (*(dtype as *const _PyArray_LegacyDescr)).$property } + } + } + }; +} + +#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] +macro_rules! DESCR_ACCESSOR { + ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $zero:expr) => { + #[allow(non_snake_case)] + #[inline(always)] + pub fn $name(dtype: *const PyArray_Descr) -> $type { + if $legacy_only && !PyDataType_ISLEGACY(dtype) { + $zero + } else { + let (_, api_version) = *ABI_API_VERSIONS + .get() + .expect("ABI_API_VERSIONS is initialized"); + if api_version < NPY_2_0_API_VERSION { + unsafe { (*(dtype as *mut PyArray_DescrProto)).$property as $type } + } else { + unsafe { (*(dtype as *const _PyArray_LegacyDescr)).$property } + } + } + } + }; +} + +DESCR_ACCESSOR!(PyDataType_ELSIZE, elsize, npy_intp, false, 0); +DESCR_ACCESSOR!(PyDataType_ALIGNMENT, alignment, npy_intp, false, 0); +DESCR_ACCESSOR!( + PyDataType_METADATA, + metadata, + *mut PyObject, + true, + std::ptr::null_mut() +); +DESCR_ACCESSOR!( + PyDataType_SUBARRAY, + subarray, + *mut PyArray_ArrayDescr, + true, + std::ptr::null_mut() +); +DESCR_ACCESSOR!( + PyDataType_NAMES, + names, + *mut PyObject, + true, + std::ptr::null_mut() +); +DESCR_ACCESSOR!( + PyDataType_FIELDS, + fields, + *mut PyObject, + true, + std::ptr::null_mut() +); +DESCR_ACCESSOR!( + PyDataType_C_METADATA, + c_metadata, + *mut NpyAuxData, + true, + std::ptr::null_mut() +); + #[repr(C)] #[derive(Copy, Clone)] pub struct PyArray_ArrayDescr { @@ -232,7 +458,10 @@ pub struct PyUFuncObject { pub type_resolver: PyUFunc_TypeResolutionFunc, pub legacy_inner_loop_selector: PyUFunc_LegacyInnerLoopSelectionFunc, pub reserved2: *mut c_void, + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] pub masked_inner_loop_selector: PyUFunc_MaskedInnerLoopSelectionFunc, + #[cfg(all(not(feature = "numpy-2"), feature = "numpy-2"))] + pub reserved3: *mut c_void, pub op_flags: *mut npy_uint32, pub iter_flags: npy_uint32, } @@ -412,3 +641,61 @@ pub struct PyArray_DatetimeDTypeMetaData { pub base: NpyAuxData, pub meta: PyArray_DatetimeMetaData, } + +// npy_packed_static_string and npy_string_allocator are opaque pointers +// consider extern types when they are stabilized +// https://github.com/rust-lang/rust/issues/43467 +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +pub type npy_packed_static_string = c_void; +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +pub type npy_string_allocator = c_void; + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +pub type PyArray_DTypeMeta = PyTypeObject; + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +#[repr(C)] +#[derive(Clone, Copy)] +pub struct npy_static_string { + size: usize, + buf: *const c_char, +} + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +#[repr(C)] +#[derive(Clone, Copy)] +pub struct PyArray_StringDTypeObject { + pub base: PyArray_Descr, + pub na_object: *mut PyObject, + pub coerce: c_char, + pub has_nan_na: c_char, + pub has_string_na: c_char, + pub array_owned: c_char, + pub default_string: npy_static_string, + pub na_name: npy_static_string, + pub allocator: *mut npy_string_allocator, +} + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +#[repr(C)] +#[derive(Clone, Copy)] +pub struct PyArrayMethod_Spec { + pub name: *const c_char, + pub nin: c_int, + pub nout: c_int, + pub casting: NPY_CASTING, + pub flags: NPY_ARRAYMETHOD_FLAGS, + pub dtypes: *mut *mut PyArray_DTypeMeta, + pub slots: *mut PyType_Slot, +} + +#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] +#[repr(C)] +#[derive(Clone, Copy)] +pub struct PyArrayDTypeMeta_Spec { + pub typeobj: *mut PyTypeObject, + pub flags: c_int, + pub casts: *mut *mut PyArrayMethod_Spec, + pub slots: *mut PyType_Slot, + pub baseclass: *mut PyTypeObject, +} diff --git a/src/npyffi/types.rs b/src/npyffi/types.rs index e147576f..a0a0203c 100644 --- a/src/npyffi/types.rs +++ b/src/npyffi/types.rs @@ -141,10 +141,16 @@ pub enum NPY_TYPES { NPY_DATETIME = 21, NPY_TIMEDELTA = 22, NPY_HALF = 23, + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] NPY_NTYPES = 24, + #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] + NPY_NTYPES_LEGACY = 24, NPY_NOTYPE = 25, + #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] NPY_CHAR = 26, NPY_USERDEF = 256, + #[cfg(feature = "numpy-2")] + NPY_VSTRING = 2056, } #[repr(u32)] @@ -270,3 +276,14 @@ impl NPY_BYTEORDER_CHAR { #[cfg(target_endian = "big")] pub const NPY_OPPBYTE: Self = Self::NPY_LITTLE; } + +#[repr(i32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum NPY_ARRAYMETHOD_FLAGS { + NPY_METH_REQUIRES_PYAPI = 1 << 0, + NPY_METH_NO_FLOATINGPOINT_ERRORS = 1 << 1, + 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 +} From 17955d32d108e3836d67b55c7d4bbbf6f3f6c40c Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Tue, 4 Jun 2024 09:57:39 +1000 Subject: [PATCH 03/11] Use the new "universal" access functions --- src/borrow/shared.rs | 4 ++-- src/datetime.rs | 6 ++++-- src/dtype.rs | 44 ++++++++++++++++++++++++-------------------- src/strings.rs | 3 ++- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/borrow/shared.rs b/src/borrow/shared.rs index 1ff4344e..439ce161 100644 --- a/src/borrow/shared.rs +++ b/src/borrow/shared.rs @@ -12,7 +12,7 @@ use rustc_hash::FxHashMap; use crate::array::get_array_module; use crate::cold; use crate::error::BorrowError; -use crate::npyffi::{PyArrayObject, PyArray_Check, NPY_ARRAY_WRITEABLE}; +use crate::npyffi::{PyArrayObject, PyArray_Check, PyDataType_ELSIZE, NPY_ARRAY_WRITEABLE}; /// Defines the shared C API used for borrow checking /// @@ -403,7 +403,7 @@ fn data_range(array: *mut PyArrayObject) -> (*mut c_char, *mut c_char) { let shape = unsafe { from_raw_parts((*array).dimensions as *mut usize, nd) }; let strides = unsafe { from_raw_parts((*array).strides, nd) }; - let itemsize = unsafe { (*(*array).descr).elsize } as isize; + let itemsize = unsafe { PyDataType_ELSIZE((*array).descr) } as isize; let mut start = 0; let mut end = 0; diff --git a/src/datetime.rs b/src/datetime.rs index 9a96799e..eb5d6c02 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -67,7 +67,9 @@ use pyo3::{sync::GILProtected, Bound, Py, Python}; use rustc_hash::FxHashMap; use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods}; -use crate::npyffi::{PyArray_DatetimeDTypeMetaData, NPY_DATETIMEUNIT, NPY_TYPES}; +use crate::npyffi::{ + PyArray_DatetimeDTypeMetaData, PyDataType_C_METADATA, NPY_DATETIMEUNIT, NPY_TYPES, +}; /// Represents the [datetime units][datetime-units] supported by NumPy /// @@ -230,7 +232,7 @@ impl TypeDescriptors { // SAFETY: `self.npy_type` is either `NPY_DATETIME` or `NPY_TIMEDELTA` which implies the type of `c_metadata`. unsafe { - let metadata = &mut *((*dtype.as_dtype_ptr()).c_metadata + let metadata = &mut *(PyDataType_C_METADATA(dtype.as_dtype_ptr()) as *mut PyArray_DatetimeDTypeMetaData); metadata.meta.base = unit; diff --git a/src/dtype.rs b/src/dtype.rs index 9aa37eab..0fe5dc53 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -1,7 +1,5 @@ use std::mem::size_of; -use std::os::raw::{ - c_char, c_int, c_long, c_longlong, c_short, c_uint, c_ulong, c_ulonglong, c_ushort, -}; +use std::os::raw::{c_int, c_long, c_longlong, c_short, c_uint, c_ulong, c_ulonglong, c_ushort}; use std::ptr; #[cfg(feature = "half")] @@ -19,8 +17,9 @@ use pyo3::{ use pyo3::{sync::GILOnceCell, Py}; use crate::npyffi::{ - NpyTypes, PyArray_Descr, NPY_ALIGNED_STRUCT, NPY_BYTEORDER_CHAR, NPY_ITEM_HASOBJECT, NPY_TYPES, - PY_ARRAY_API, + FlagType, 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, }; pub use num_complex::{Complex32, Complex64}; @@ -256,7 +255,7 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.flags`][dtype-flags]. /// /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html - pub fn flags(&self) -> c_char { + pub fn flags(&self) -> FlagType { self.as_borrowed().flags() } @@ -397,7 +396,7 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// [dtype-itemsiize]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.itemsize.html fn itemsize(&self) -> usize { - unsafe { *self.as_dtype_ptr() }.elsize.max(0) as _ + PyDataType_ELSIZE(self.as_dtype_ptr()).max(0) as _ } /// Returns the required alignment (bytes) of this type descriptor according to the compiler. @@ -406,7 +405,7 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// /// [dtype-alignment]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.alignment.html fn alignment(&self) -> usize { - unsafe { *self.as_dtype_ptr() }.alignment.max(0) as _ + PyDataType_ALIGNMENT(self.as_dtype_ptr()).max(0) as _ } /// Returns an ASCII character indicating the byte-order of this type descriptor object. @@ -447,8 +446,8 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.flags`][dtype-flags]. /// /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html - fn flags(&self) -> c_char { - unsafe { *self.as_dtype_ptr() }.flags + fn flags(&self) -> FlagType { + PyDataType_FLAGS(self.as_dtype_ptr()) } /// Returns the number of dimensions if this type descriptor represents a sub-array, and zero otherwise. @@ -460,7 +459,7 @@ pub trait PyArrayDescrMethods<'py>: Sealed { if !self.has_subarray() { return 0; } - unsafe { PyTuple_Size((*((*self.as_dtype_ptr()).subarray)).shape).max(0) as _ } + unsafe { PyTuple_Size((*PyDataType_SUBARRAY(self.as_dtype_ptr())).shape).max(0) as _ } } /// Returns the type descriptor for the base element of subarrays, regardless of their dimension or shape. @@ -505,13 +504,13 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Returns true if the type descriptor is a sub-array. fn has_subarray(&self) -> bool { // equivalent to PyDataType_HASSUBARRAY(self) - unsafe { !(*self.as_dtype_ptr()).subarray.is_null() } + !PyDataType_SUBARRAY(self.as_dtype_ptr()).is_null() } /// Returns true if the type descriptor is a structured type. fn has_fields(&self) -> bool { // equivalent to PyDataType_HASFIELDS(self) - unsafe { !(*self.as_dtype_ptr()).names.is_null() } + !PyDataType_NAMES(self.as_dtype_ptr()).is_null() } /// Returns true if type descriptor byteorder is native, or `None` if not applicable. @@ -581,8 +580,11 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { self.clone() } else { unsafe { - Bound::from_borrowed_ptr(self.py(), (*(*self.as_dtype_ptr()).subarray).base.cast()) - .downcast_into_unchecked() + Bound::from_borrowed_ptr( + self.py(), + (*PyDataType_SUBARRAY(self.as_dtype_ptr())).base.cast(), + ) + .downcast_into_unchecked() } } } @@ -592,9 +594,11 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { Vec::new() } else { // NumPy guarantees that shape is a tuple of non-negative integers so this should never panic. - unsafe { Borrowed::from_ptr(self.py(), (*(*self.as_dtype_ptr()).subarray).shape) } - .extract() - .unwrap() + unsafe { + Borrowed::from_ptr(self.py(), (*PyDataType_SUBARRAY(self.as_dtype_ptr())).shape) + } + .extract() + .unwrap() } } @@ -602,7 +606,7 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { if !self.has_fields() { return None; } - let names = unsafe { Borrowed::from_ptr(self.py(), (*self.as_dtype_ptr()).names) }; + let names = unsafe { Borrowed::from_ptr(self.py(), PyDataType_NAMES(self.as_dtype_ptr())) }; names.extract().ok() } @@ -612,7 +616,7 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { "cannot get field information: type descriptor has no fields", )); } - let dict = unsafe { Borrowed::from_ptr(self.py(), (*self.as_dtype_ptr()).fields) }; + let dict = unsafe { Borrowed::from_ptr(self.py(), PyDataType_FIELDS(self.as_dtype_ptr())) }; let dict = unsafe { dict.downcast_unchecked::() }; // NumPy guarantees that fields are tuples of proper size and type, so this should never panic. let tuple = dict diff --git a/src/strings.rs b/src/strings.rs index 74606289..66cc0b15 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -18,6 +18,7 @@ use pyo3::{ use rustc_hash::FxHashMap; use crate::dtype::{Element, PyArrayDescr, PyArrayDescrMethods}; +use crate::npyffi::PyDataType_SET_ELSIZE; use crate::npyffi::NPY_TYPES; /// A newtype wrapper around [`[u8; N]`][Py_UCS1] to handle [`byte` scalars][numpy-bytes] while satisfying coherence. @@ -183,7 +184,7 @@ impl TypeDescriptors { let dtype = PyArrayDescr::new_from_npy_type(py, npy_type); let descr = &mut *dtype.as_dtype_ptr(); - descr.elsize = size.try_into().unwrap(); + PyDataType_SET_ELSIZE(descr, size.try_into().unwrap()); descr.byteorder = byteorder; entry.insert(dtype.into()) From 7a5a56795af402cac75667694cde665302ffe28c Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Tue, 4 Jun 2024 17:03:18 +1000 Subject: [PATCH 04/11] Fix runtime tests --- src/npyffi/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/npyffi/mod.rs b/src/npyffi/mod.rs index 56a76bad..b4a27607 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -51,7 +51,7 @@ fn get_numpy_api<'py>( (*PyArray_GetNDArrayCFeatureVersion)() }; #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - if api_version < NPY_2_0_API_VERSION { + if api_version >= NPY_2_0_API_VERSION { panic!( "the extension was compiled for numpy 1.x but the runtime version is 2.x (ABI {:08x}.{:08x})", abi_version, @@ -59,7 +59,7 @@ fn get_numpy_api<'py>( ); } #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - if api_version >= NPY_2_0_API_VERSION { + if api_version < NPY_2_0_API_VERSION { panic!( "the extension was compiled for numpy 2.x but the runtime version is 1.x (ABI {:08x}.{:08x})", abi_version, From 2014e2a7973ae7706ac37a65cf9b45c4013dfeab Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Fri, 7 Jun 2024 20:11:34 +1000 Subject: [PATCH 05/11] Remove feature flags and always check the version at runtime where appropriate --- Cargo.toml | 5 -- src/borrow/shared.rs | 52 ++++++------- src/datetime.rs | 2 +- src/dtype.rs | 147 +++++++++++++++++++----------------- src/npyffi/array.rs | 168 +++++++++++++----------------------------- src/npyffi/flags.rs | 27 +++---- src/npyffi/mod.rs | 100 +++++++++++++++---------- src/npyffi/objects.rs | 134 ++++++--------------------------- src/npyffi/types.rs | 5 -- src/strings.rs | 2 +- 10 files changed, 253 insertions(+), 389 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0bf5f917..4c71108a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,3 @@ nalgebra = { version = "0.32", default-features = false, features = ["std"] } [package.metadata.docs.rs] all-features = true - -[features] -default = ["numpy-1", "numpy-2"] -numpy-1 = [] -numpy-2 = [] diff --git a/src/borrow/shared.rs b/src/borrow/shared.rs index 439ce161..38f8963e 100644 --- a/src/borrow/shared.rs +++ b/src/borrow/shared.rs @@ -48,7 +48,7 @@ unsafe extern "C" fn acquire_shared(flags: *mut c_void, array: *mut PyArrayObjec let flags = &mut *(flags as *mut BorrowFlags); let address = base_address(py, array); - let key = borrow_key(array); + let key = borrow_key(py, array); match flags.acquire(address, key) { Ok(()) => 0, @@ -66,7 +66,7 @@ unsafe extern "C" fn acquire_mut_shared(flags: *mut c_void, array: *mut PyArrayO let flags = &mut *(flags as *mut BorrowFlags); let address = base_address(py, array); - let key = borrow_key(array); + let key = borrow_key(py, array); match flags.acquire_mut(address, key) { Ok(()) => 0, @@ -80,7 +80,7 @@ unsafe extern "C" fn release_shared(flags: *mut c_void, array: *mut PyArrayObjec let flags = &mut *(flags as *mut BorrowFlags); let address = base_address(py, array); - let key = borrow_key(array); + let key = borrow_key(py, array); flags.release(address, key); } @@ -91,7 +91,7 @@ unsafe extern "C" fn release_mut_shared(flags: *mut c_void, array: *mut PyArrayO let flags = &mut *(flags as *mut BorrowFlags); let address = base_address(py, array); - let key = borrow_key(array); + let key = borrow_key(py, array); flags.release_mut(address, key); } @@ -379,8 +379,8 @@ fn base_address<'py>(py: Python<'py>, mut array: *mut PyArrayObject) -> *mut c_v } } -fn borrow_key(array: *mut PyArrayObject) -> BorrowKey { - let range = data_range(array); +fn borrow_key<'py>(py: Python<'py>, array: *mut PyArrayObject) -> BorrowKey { + let range = data_range(py, array); let data_ptr = unsafe { (*array).data }; let gcd_strides = gcd_strides(array); @@ -392,7 +392,7 @@ fn borrow_key(array: *mut PyArrayObject) -> BorrowKey { } } -fn data_range(array: *mut PyArrayObject) -> (*mut c_char, *mut c_char) { +fn data_range<'py>(py: Python<'py>, array: *mut PyArrayObject) -> (*mut c_char, *mut c_char) { let nd = unsafe { (*array).nd } as usize; let data = unsafe { (*array).data }; @@ -403,7 +403,7 @@ fn data_range(array: *mut PyArrayObject) -> (*mut c_char, *mut c_char) { let shape = unsafe { from_raw_parts((*array).dimensions as *mut usize, nd) }; let strides = unsafe { from_raw_parts((*array).strides, nd) }; - let itemsize = unsafe { PyDataType_ELSIZE((*array).descr) } as isize; + let itemsize = unsafe { PyDataType_ELSIZE(py, (*array).descr) } as isize; let mut start = 0; let mut end = 0; @@ -468,7 +468,7 @@ mod tests { let base_address = base_address(py, array.as_array_ptr()); assert_eq!(base_address, array.as_ptr().cast()); - let data_range = data_range(array.as_array_ptr()); + let data_range = data_range(py, array.as_array_ptr()); assert_eq!(data_range.0, array.data() as *mut c_char); assert_eq!(data_range.1, unsafe { array.data().add(6) } as *mut c_char); }); @@ -486,7 +486,7 @@ mod tests { assert_ne!(base_address, array.as_ptr().cast()); assert_eq!(base_address, base.cast::()); - let data_range = data_range(array.as_array_ptr()); + let data_range = data_range(py, array.as_array_ptr()); assert_eq!(data_range.0, array.data().cast::()); assert_eq!(data_range.1, unsafe { array.data().add(6).cast::() @@ -517,7 +517,7 @@ mod tests { assert_ne!(base_address, view.as_ptr().cast::()); assert_eq!(base_address, base.cast::()); - let data_range = data_range(view.as_array_ptr()); + let data_range = data_range(py, view.as_array_ptr()); assert_eq!(data_range.0, array.data() as *mut c_char); assert_eq!(data_range.1, unsafe { array.data().add(4) } as *mut c_char); }); @@ -550,7 +550,7 @@ mod tests { assert_ne!(base_address, array.as_ptr().cast::()); assert_eq!(base_address, base.cast::()); - let data_range = data_range(view.as_array_ptr()); + let data_range = data_range(py, view.as_array_ptr()); assert_eq!(data_range.0, array.data().cast::()); assert_eq!(data_range.1, unsafe { array.data().add(4).cast::() @@ -600,7 +600,7 @@ mod tests { assert_ne!(base_address, view1.as_ptr().cast::()); assert_eq!(base_address, base as *mut c_void); - let data_range = data_range(view2.as_array_ptr()); + let data_range = data_range(py, view2.as_array_ptr()); assert_eq!(data_range.0, array.data() as *mut c_char); assert_eq!(data_range.1, unsafe { array.data().add(1) } as *mut c_char); }); @@ -652,7 +652,7 @@ mod tests { assert_ne!(base_address, array.as_ptr().cast::()); assert_eq!(base_address, base.cast::()); - let data_range = data_range(view2.as_array_ptr()); + let data_range = data_range(py, view2.as_array_ptr()); assert_eq!(data_range.0, array.data().cast::()); assert_eq!(data_range.1, unsafe { array.data().add(1).cast::() @@ -683,7 +683,7 @@ mod tests { assert_ne!(base_address, view.as_ptr().cast::()); assert_eq!(base_address, base.cast::()); - let data_range = data_range(view.as_array_ptr()); + let data_range = data_range(py, view.as_array_ptr()); assert_eq!(view.data(), unsafe { array.data().offset(2) }); assert_eq!(data_range.0, unsafe { view.data().offset(-2) } as *mut c_char); @@ -703,7 +703,7 @@ mod tests { let base_address = base_address(py, array.as_array_ptr()); assert_eq!(base_address, array.as_ptr().cast::()); - let data_range = data_range(array.as_array_ptr()); + let data_range = data_range(py, array.as_array_ptr()); assert_eq!(data_range.0, array.data() as *mut c_char); assert_eq!(data_range.1, array.data() as *mut c_char); }); @@ -721,7 +721,7 @@ mod tests { .downcast_into::>() .unwrap(); - let key1 = borrow_key(view1.as_array_ptr()); + let key1 = borrow_key(py, view1.as_array_ptr()); assert_eq!(view1.strides(), &[80, 24]); assert_eq!(key1.gcd_strides, 8); @@ -732,7 +732,7 @@ mod tests { .downcast_into::>() .unwrap(); - let key2 = borrow_key(view2.as_array_ptr()); + let key2 = borrow_key(py, view2.as_array_ptr()); assert_eq!(view2.strides(), &[80, 24]); assert_eq!(key2.gcd_strides, 8); @@ -743,7 +743,7 @@ mod tests { .downcast_into::>() .unwrap(); - let key3 = borrow_key(view3.as_array_ptr()); + let key3 = borrow_key(py, view3.as_array_ptr()); assert_eq!(view3.strides(), &[80, 16]); assert_eq!(key3.gcd_strides, 16); @@ -754,7 +754,7 @@ mod tests { .downcast_into::>() .unwrap(); - let key4 = borrow_key(view4.as_array_ptr()); + let key4 = borrow_key(py, view4.as_array_ptr()); assert_eq!(view4.strides(), &[80, 16]); assert_eq!(key4.gcd_strides, 16); @@ -777,7 +777,7 @@ mod tests { let base1 = base_address(py, array1.as_array_ptr()); let base2 = base_address(py, array2.as_array_ptr()); - let key1 = borrow_key(array1.as_array_ptr()); + let key1 = borrow_key(py, array1.as_array_ptr()); let _exclusive1 = array1.readwrite(); { @@ -791,7 +791,7 @@ mod tests { assert_eq!(flag, -1); } - let key2 = borrow_key(array2.as_array_ptr()); + let key2 = borrow_key(py, array2.as_array_ptr()); let _shared2 = array2.readonly(); { @@ -827,7 +827,7 @@ mod tests { .downcast_into::>() .unwrap(); - let key1 = borrow_key(view1.as_array_ptr()); + let key1 = borrow_key(py, view1.as_array_ptr()); let exclusive1 = view1.readwrite(); { @@ -847,7 +847,7 @@ mod tests { .downcast_into::>() .unwrap(); - let key2 = borrow_key(view2.as_array_ptr()); + let key2 = borrow_key(py, view2.as_array_ptr()); let shared2 = view2.readonly(); { @@ -870,7 +870,7 @@ mod tests { .downcast_into::>() .unwrap(); - let key3 = borrow_key(view3.as_array_ptr()); + let key3 = borrow_key(py, view3.as_array_ptr()); let shared3 = view3.readonly(); { @@ -896,7 +896,7 @@ mod tests { .downcast_into::>() .unwrap(); - let key4 = borrow_key(view4.as_array_ptr()); + let key4 = borrow_key(py, view4.as_array_ptr()); let shared4 = view4.readonly(); { diff --git a/src/datetime.rs b/src/datetime.rs index eb5d6c02..79417061 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -232,7 +232,7 @@ impl TypeDescriptors { // SAFETY: `self.npy_type` is either `NPY_DATETIME` or `NPY_TIMEDELTA` which implies the type of `c_metadata`. unsafe { - let metadata = &mut *(PyDataType_C_METADATA(dtype.as_dtype_ptr()) + let metadata = &mut *(PyDataType_C_METADATA(py, dtype.as_dtype_ptr()) as *mut PyArray_DatetimeDTypeMetaData); metadata.meta.base = unit; diff --git a/src/dtype.rs b/src/dtype.rs index 0fe5dc53..17a6b747 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -17,7 +17,7 @@ use pyo3::{ use pyo3::{sync::GILOnceCell, Py}; use crate::npyffi::{ - FlagType, NpyTypes, PyArray_Descr, PyDataType_ALIGNMENT, PyDataType_ELSIZE, PyDataType_FIELDS, + 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, }; @@ -204,8 +204,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.itemsize`][dtype-itemsize]. /// /// [dtype-itemsiize]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.itemsize.html - pub fn itemsize(&self) -> usize { - self.as_borrowed().itemsize() + pub fn itemsize<'py>(&self, py: Python<'py>) -> usize { + self.as_borrowed().itemsize(py) } /// Returns the required alignment (bytes) of this type descriptor according to the compiler. @@ -213,8 +213,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.alignment`][dtype-alignment]. /// /// [dtype-alignment]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.alignment.html - pub fn alignment(&self) -> usize { - self.as_borrowed().alignment() + pub fn alignment<'py>(&self, py: Python<'py>) -> usize { + self.as_borrowed().alignment(py) } /// Returns an ASCII character indicating the byte-order of this type descriptor object. @@ -255,8 +255,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.flags`][dtype-flags]. /// /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html - pub fn flags(&self) -> FlagType { - self.as_borrowed().flags() + pub fn flags<'py>(&self, py: Python<'py>) -> u64 { + self.as_borrowed().flags(py) } /// Returns the number of dimensions if this type descriptor represents a sub-array, and zero otherwise. @@ -264,8 +264,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.ndim`][dtype-ndim]. /// /// [dtype-ndim]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.ndim.html - pub fn ndim(&self) -> usize { - self.as_borrowed().ndim() + pub fn ndim<'py>(&self, py: Python<'py>) -> usize { + self.as_borrowed().ndim(py) } /// Returns the type descriptor for the base element of subarrays, regardless of their dimension or shape. @@ -295,8 +295,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.hasobject`][dtype-hasobject]. /// /// [dtype-hasobject]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.hasobject.html - pub fn has_object(&self) -> bool { - self.as_borrowed().has_object() + pub fn has_object<'py>(&self, py: Python<'py>) -> bool { + self.as_borrowed().has_object(py) } /// Returns true if the type descriptor is a struct which maintains field alignment. @@ -307,18 +307,18 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.isalignedstruct`][dtype-isalignedstruct]. /// /// [dtype-isalignedstruct]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.isalignedstruct.html - pub fn is_aligned_struct(&self) -> bool { - self.as_borrowed().is_aligned_struct() + pub fn is_aligned_struct<'py>(&self, py: Python<'py>) -> bool { + self.as_borrowed().is_aligned_struct(py) } /// Returns true if the type descriptor is a sub-array. - pub fn has_subarray(&self) -> bool { - self.as_borrowed().has_subarray() + pub fn has_subarray<'py>(&self, py: Python<'py>) -> bool { + self.as_borrowed().has_subarray(py) } /// Returns true if the type descriptor is a structured type. - pub fn has_fields(&self) -> bool { - self.as_borrowed().has_fields() + pub fn has_fields<'py>(&self, py: Python<'py>) -> bool { + self.as_borrowed().has_fields(py) } /// Returns true if type descriptor byteorder is native, or `None` if not applicable. @@ -395,8 +395,8 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// /// [dtype-itemsiize]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.itemsize.html - fn itemsize(&self) -> usize { - PyDataType_ELSIZE(self.as_dtype_ptr()).max(0) as _ + fn itemsize(&self, py: Python<'py>) -> usize { + unsafe { PyDataType_ELSIZE(py, self.as_dtype_ptr()).max(0) as _ } } /// Returns the required alignment (bytes) of this type descriptor according to the compiler. @@ -404,8 +404,8 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.alignment`][dtype-alignment]. /// /// [dtype-alignment]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.alignment.html - fn alignment(&self) -> usize { - PyDataType_ALIGNMENT(self.as_dtype_ptr()).max(0) as _ + fn alignment(&self, py: Python<'py>) -> usize { + unsafe { PyDataType_ALIGNMENT(py, self.as_dtype_ptr()).max(0) as _ } } /// Returns an ASCII character indicating the byte-order of this type descriptor object. @@ -446,8 +446,8 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.flags`][dtype-flags]. /// /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html - fn flags(&self) -> FlagType { - PyDataType_FLAGS(self.as_dtype_ptr()) + fn flags(&self, py: Python<'py>) -> u64 { + unsafe { PyDataType_FLAGS(py, self.as_dtype_ptr()) } } /// Returns the number of dimensions if this type descriptor represents a sub-array, and zero otherwise. @@ -455,11 +455,11 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.ndim`][dtype-ndim]. /// /// [dtype-ndim]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.ndim.html - fn ndim(&self) -> usize { - if !self.has_subarray() { + fn ndim(&self, py: Python<'py>) -> usize { + if !self.has_subarray(py) { return 0; } - unsafe { PyTuple_Size((*PyDataType_SUBARRAY(self.as_dtype_ptr())).shape).max(0) as _ } + unsafe { PyTuple_Size((*PyDataType_SUBARRAY(py, self.as_dtype_ptr())).shape).max(0) as _ } } /// Returns the type descriptor for the base element of subarrays, regardless of their dimension or shape. @@ -485,8 +485,8 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.hasobject`][dtype-hasobject]. /// /// [dtype-hasobject]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.hasobject.html - fn has_object(&self) -> bool { - self.flags() & NPY_ITEM_HASOBJECT != 0 + fn has_object(&self, py: Python<'py>) -> bool { + self.flags(py) & NPY_ITEM_HASOBJECT != 0 } /// Returns true if the type descriptor is a struct which maintains field alignment. @@ -497,20 +497,20 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.isalignedstruct`][dtype-isalignedstruct]. /// /// [dtype-isalignedstruct]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.isalignedstruct.html - fn is_aligned_struct(&self) -> bool { - self.flags() & NPY_ALIGNED_STRUCT != 0 + fn is_aligned_struct(&self, py: Python<'py>) -> bool { + self.flags(py) & NPY_ALIGNED_STRUCT != 0 } /// Returns true if the type descriptor is a sub-array. - fn has_subarray(&self) -> bool { + fn has_subarray(&self, py: Python<'py>) -> bool { // equivalent to PyDataType_HASSUBARRAY(self) - !PyDataType_SUBARRAY(self.as_dtype_ptr()).is_null() + unsafe { !PyDataType_SUBARRAY(py, self.as_dtype_ptr()).is_null() } } /// Returns true if the type descriptor is a structured type. - fn has_fields(&self) -> bool { + fn has_fields(&self, py: Python<'py>) -> bool { // equivalent to PyDataType_HASFIELDS(self) - !PyDataType_NAMES(self.as_dtype_ptr()).is_null() + unsafe { !PyDataType_NAMES(py, self.as_dtype_ptr()).is_null() } } /// Returns true if type descriptor byteorder is native, or `None` if not applicable. @@ -576,13 +576,15 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { } fn base(&self) -> Bound<'py, PyArrayDescr> { - if !self.has_subarray() { + if !self.has_subarray(self.py()) { self.clone() } else { unsafe { Bound::from_borrowed_ptr( self.py(), - (*PyDataType_SUBARRAY(self.as_dtype_ptr())).base.cast(), + (*PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr())) + .base + .cast(), ) .downcast_into_unchecked() } @@ -590,12 +592,15 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { } fn shape(&self) -> Vec { - if !self.has_subarray() { + if !self.has_subarray(self.py()) { Vec::new() } else { // NumPy guarantees that shape is a tuple of non-negative integers so this should never panic. unsafe { - Borrowed::from_ptr(self.py(), (*PyDataType_SUBARRAY(self.as_dtype_ptr())).shape) + Borrowed::from_ptr( + self.py(), + (*PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr())).shape, + ) } .extract() .unwrap() @@ -603,20 +608,24 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { } fn names(&self) -> Option> { - if !self.has_fields() { + if !self.has_fields(self.py()) { return None; } - let names = unsafe { Borrowed::from_ptr(self.py(), PyDataType_NAMES(self.as_dtype_ptr())) }; + let names = unsafe { + Borrowed::from_ptr(self.py(), PyDataType_NAMES(self.py(), self.as_dtype_ptr())) + }; names.extract().ok() } fn get_field(&self, name: &str) -> PyResult<(Bound<'py, PyArrayDescr>, usize)> { - if !self.has_fields() { + if !self.has_fields(self.py()) { return Err(PyValueError::new_err( "cannot get field information: type descriptor has no fields", )); } - let dict = unsafe { Borrowed::from_ptr(self.py(), PyDataType_FIELDS(self.as_dtype_ptr())) }; + let dict = unsafe { + Borrowed::from_ptr(self.py(), PyDataType_FIELDS(self.py(), self.as_dtype_ptr())) + }; let dict = unsafe { dict.downcast_unchecked::() }; // NumPy guarantees that fields are tuples of proper size and type, so this should never panic. let tuple = dict @@ -821,7 +830,7 @@ mod tests { let dt = PyArrayDescr::new_bound(py, [("a", "O"), ("b", "?")].as_ref()).unwrap(); assert_eq!(dt.names(), Some(vec!["a".to_owned(), "b".to_owned()])); - assert!(dt.has_object()); + assert!(dt.has_object(py)); assert!(dt .get_field("a") .unwrap() @@ -875,21 +884,21 @@ mod tests { let dt = dtype_bound::(py); assert_eq!(dt.num(), NPY_TYPES::NPY_DOUBLE as c_int); - assert_eq!(dt.flags(), 0); + assert_eq!(dt.flags(py), 0); assert_eq!(dt.typeobj().qualname().unwrap(), "float64"); assert_eq!(dt.char(), b'd'); assert_eq!(dt.kind(), b'f'); assert_eq!(dt.byteorder(), b'='); assert_eq!(dt.is_native_byteorder(), Some(true)); - assert_eq!(dt.itemsize(), 8); - assert_eq!(dt.alignment(), 8); - assert!(!dt.has_object()); + assert_eq!(dt.itemsize(py), 8); + assert_eq!(dt.alignment(py), 8); + assert!(!dt.has_object(py)); assert!(dt.names().is_none()); - assert!(!dt.has_fields()); - assert!(!dt.is_aligned_struct()); - assert!(!dt.has_subarray()); + assert!(!dt.has_fields(py)); + assert!(!dt.is_aligned_struct(py)); + assert!(!dt.has_subarray(py)); assert!(dt.base().is_equiv_to(&dt)); - assert_eq!(dt.ndim(), 0); + assert_eq!(dt.ndim(py), 0); assert_eq!(dt.shape(), vec![]); }); } @@ -911,20 +920,20 @@ mod tests { .unwrap(); assert_eq!(dt.num(), NPY_TYPES::NPY_VOID as c_int); - assert_eq!(dt.flags(), 0); + assert_eq!(dt.flags(py), 0); assert_eq!(dt.typeobj().qualname().unwrap(), "void"); assert_eq!(dt.char(), b'V'); assert_eq!(dt.kind(), b'V'); assert_eq!(dt.byteorder(), b'|'); assert_eq!(dt.is_native_byteorder(), None); - assert_eq!(dt.itemsize(), 48); - assert_eq!(dt.alignment(), 8); - assert!(!dt.has_object()); + assert_eq!(dt.itemsize(py), 48); + assert_eq!(dt.alignment(py), 8); + assert!(!dt.has_object(py)); assert!(dt.names().is_none()); - assert!(!dt.has_fields()); - assert!(!dt.is_aligned_struct()); - assert!(dt.has_subarray()); - assert_eq!(dt.ndim(), 2); + assert!(!dt.has_fields(py)); + assert!(!dt.is_aligned_struct(py)); + assert!(dt.has_subarray(py)); + assert_eq!(dt.ndim(py), 2); assert_eq!(dt.shape(), vec![2, 3]); assert!(dt.base().is_equiv_to(&dtype_bound::(py))); }); @@ -947,25 +956,25 @@ mod tests { .unwrap(); assert_eq!(dt.num(), NPY_TYPES::NPY_VOID as c_int); - assert_ne!(dt.flags() & NPY_ITEM_HASOBJECT, 0); - assert_ne!(dt.flags() & NPY_NEEDS_PYAPI, 0); - assert_ne!(dt.flags() & NPY_ALIGNED_STRUCT, 0); + assert_ne!(dt.flags(py) & NPY_ITEM_HASOBJECT, 0); + assert_ne!(dt.flags(py) & NPY_NEEDS_PYAPI, 0); + assert_ne!(dt.flags(py) & NPY_ALIGNED_STRUCT, 0); assert_eq!(dt.typeobj().qualname().unwrap(), "void"); assert_eq!(dt.char(), b'V'); assert_eq!(dt.kind(), b'V'); assert_eq!(dt.byteorder(), b'|'); assert_eq!(dt.is_native_byteorder(), None); - assert_eq!(dt.itemsize(), 24); - assert_eq!(dt.alignment(), 8); - assert!(dt.has_object()); + assert_eq!(dt.itemsize(py), 24); + assert_eq!(dt.alignment(py), 8); + assert!(dt.has_object(py)); assert_eq!( dt.names(), Some(vec!["x".to_owned(), "y".to_owned(), "z".to_owned()]) ); - assert!(dt.has_fields()); - assert!(dt.is_aligned_struct()); - assert!(!dt.has_subarray()); - assert_eq!(dt.ndim(), 0); + assert!(dt.has_fields(py)); + assert!(dt.is_aligned_struct(py)); + assert!(!dt.has_subarray(py)); + assert_eq!(dt.ndim(py), 0); assert_eq!(dt.shape(), vec![]); assert!(dt.base().is_equiv_to(&dt)); let x = dt.get_field("x").unwrap(); diff --git a/src/npyffi/array.rs b/src/npyffi/array.rs index 224b724f..2e4192fc 100644 --- a/src/npyffi/array.rs +++ b/src/npyffi/array.rs @@ -68,14 +68,7 @@ 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![50; PyArray_CastTo(out: *mut PyArrayObject, mp: *mut PyArrayObject) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![50; PyArray_CopyInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![51; PyArray_CastAnyTo(out: *mut PyArrayObject, mp: *mut PyArrayObject) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![51; PyArray_CopyAnyInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; + impl_api![50; ..=1.26; PyArray_CastTo(out: *mut PyArrayObject, mp: *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]; @@ -89,16 +82,11 @@ 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![65; PyArray_ScalarFromObject(object: *mut PyObject) -> *mut PyObject]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![65; PyArray_Pack(descr: *mut PyArray_Descr, item: *mut c_void, value: *const PyObject) -> *mut PyObject]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![66; PyArray_GetCastFunc(descr: *mut PyArray_Descr, type_num: c_int) -> PyArray_VectorUnaryFunc]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![67; PyArray_FromDims(nd: c_int, d: *mut c_int, type_: c_int) -> *mut PyObject]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![68; PyArray_FromDimsAndDataAndDescr(nd: c_int, d: *mut c_int, descr: *mut PyArray_Descr, data: *mut c_char) -> *mut PyObject]; + impl_api![65; ..=1.26; PyArray_ScalarFromObject(object: *mut PyObject) -> *mut PyObject]; + impl_api![65; 2.0..; PyArray_Pack(descr: *mut PyArray_Descr, item: *mut c_void, value: *const PyObject) -> *mut PyObject]; + impl_api![66; ..=1.26; PyArray_GetCastFunc(descr: *mut PyArray_Descr, type_num: c_int) -> PyArray_VectorUnaryFunc]; + impl_api![67; ..=1.26; PyArray_FromDims(nd: c_int, d: *mut c_int, type_: c_int) -> *mut PyObject]; + impl_api![68; ..=1.26; PyArray_FromDimsAndDataAndDescr(nd: c_int, d: *mut c_int, descr: *mut PyArray_Descr, data: *mut c_char) -> *mut PyObject]; 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]; @@ -111,12 +99,7 @@ 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![81; PyArray_MoveInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![82; PyArray_CopyInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![83; PyArray_CopyAnyInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; + impl_api![81; ..=1.26; PyArray_MoveInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; 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]; @@ -136,8 +119,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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![103; PyArray_FillObjectArray(arr: *mut PyArrayObject, obj: *mut PyObject)]; + impl_api![103; ..=1.26; PyArray_FillObjectArray(arr: *mut PyArrayObject, obj: *mut PyObject)]; 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]; @@ -149,17 +131,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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![115; PyArray_NewFlagsObject(obj: *mut PyObject) -> *mut PyObject]; + impl_api![115; ..=1.26; PyArray_NewFlagsObject(obj: *mut PyObject) -> *mut PyObject]; impl_api![116; PyArray_CanCastScalar(from: *mut PyTypeObject, to: *mut PyTypeObject) -> npy_bool]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![117; PyArray_CompareUCS4(s1: *mut npy_ucs4, s2: *mut npy_ucs4, len: usize) -> c_int]; + impl_api![117; ..=1.26; PyArray_CompareUCS4(s1: *mut npy_ucs4, s2: *mut npy_ucs4, len: usize) -> c_int]; 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)]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![122; PyArray_FieldNames(fields: *mut PyObject) -> *mut PyObject]; + impl_api![122; ..=1.26; PyArray_FieldNames(fields: *mut PyObject) -> *mut PyObject]; 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]; @@ -200,21 +179,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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![163; PyArray_As1D(op: *mut *mut PyObject, ptr: *mut *mut c_char, d1: *mut c_int, typecode: c_int) -> c_int]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![164; 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]; + impl_api![163; ..=1.26; PyArray_As1D(op: *mut *mut PyObject, ptr: *mut *mut c_char, d1: *mut c_int, typecode: c_int) -> c_int]; + impl_api![164; ..=1.26; 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]; 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![171; PyArray_CopyAndTranspose(op: *mut PyObject) -> *mut PyObject]; + impl_api![171; ..=1.26; PyArray_CopyAndTranspose(op: *mut PyObject) -> *mut PyObject]; impl_api![172; PyArray_Correlate(op1: *mut PyObject, op2: *mut PyObject, mode: c_int) -> *mut PyObject]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![173; PyArray_TypestrConvert(itemsize: c_int, gentype: c_int) -> c_int]; + impl_api![173; ..=1.26; PyArray_TypestrConvert(itemsize: c_int, gentype: c_int) -> c_int]; 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]; @@ -238,37 +213,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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![197; PyArray_ElementFromName(str: *mut c_char) -> c_int]; + impl_api![197; ..=1.26; PyArray_ElementFromName(str: *mut c_char) -> c_int]; 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![201; _PyArray_SigintHandler(signum: c_int)]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![202; _PyArray_GetSigintBuf() -> *mut c_void]; + impl_api![201; ..=1.26; _PyArray_SigintHandler(signum: c_int)]; + impl_api![202; ..=1.26; _PyArray_GetSigintBuf() -> *mut c_void]; 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![208; PyArray_CompareString(s1: *mut c_char, s2: *mut c_char, len: usize) -> c_int]; + impl_api![208; ..=1.26; PyArray_CompareString(s1: *mut c_char, s2: *mut c_char, len: usize) -> c_int]; // 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![219; PyArray_SetDatetimeParseFunction(op: *mut PyObject)]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![220; PyArray_DatetimeToDatetimeStruct(val: npy_datetime, fr: NPY_DATETIMEUNIT, result: *mut npy_datetimestruct)]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![221; PyArray_TimedeltaToTimedeltaStruct(val: npy_timedelta, fr: NPY_DATETIMEUNIT, result: *mut npy_timedeltastruct)]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![222; PyArray_DatetimeStructToDatetime(fr: NPY_DATETIMEUNIT, d: *mut npy_datetimestruct) -> npy_datetime]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![223; PyArray_TimedeltaStructToTimedelta(fr: NPY_DATETIMEUNIT, d: *mut npy_timedeltastruct) -> npy_datetime]; + impl_api![219; ..=1.26; PyArray_SetDatetimeParseFunction(op: *mut PyObject)]; + impl_api![220; ..=1.26; PyArray_DatetimeToDatetimeStruct(val: npy_datetime, fr: NPY_DATETIMEUNIT, result: *mut npy_datetimestruct)]; + impl_api![221; ..=1.26; PyArray_TimedeltaToTimedeltaStruct(val: npy_timedelta, fr: NPY_DATETIMEUNIT, result: *mut npy_timedeltastruct)]; + impl_api![222; ..=1.26; PyArray_DatetimeStructToDatetime(fr: NPY_DATETIMEUNIT, d: *mut npy_datetimestruct) -> npy_datetime]; + impl_api![223; ..=1.26; PyArray_TimedeltaStructToTimedelta(fr: NPY_DATETIMEUNIT, d: *mut npy_timedeltastruct) -> npy_datetime]; 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]; @@ -323,8 +289,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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![278; 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]; + impl_api![278;..=1.26; 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]; 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]; @@ -337,63 +302,39 @@ 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![291; PyDataMem_SetEventHook(newhook: PyDataMem_EventHookFunc, user_data: *mut c_void, old_data: *mut *mut c_void) -> PyDataMem_EventHookFunc]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![293; PyArray_MapIterSwapAxes(mit: *mut PyArrayMapIterObject, ret: *mut *mut PyArrayObject, getmap: c_int)]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![294; PyArray_MapIterArray(a: *mut PyArrayObject, index: *mut PyObject) -> *mut PyObject]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![295; PyArray_MapIterNext(mit: *mut PyArrayMapIterObject)]; + impl_api![291; ..=1.26; PyDataMem_SetEventHook(newhook: PyDataMem_EventHookFunc, user_data: *mut c_void, old_data: *mut *mut c_void) -> PyDataMem_EventHookFunc]; + impl_api![293; ..=1.26; PyArray_MapIterSwapAxes(mit: *mut PyArrayMapIterObject, ret: *mut *mut PyArrayObject, getmap: c_int)]; + impl_api![294; ..=1.26; PyArray_MapIterArray(a: *mut PyArrayObject, index: *mut PyObject) -> *mut PyObject]; + impl_api![295; ..=1.26; PyArray_MapIterNext(mit: *mut PyArrayMapIterObject)]; 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]; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - impl_api![301; PyArray_MapIterArrayCopyIfOverlap(a: *mut PyArrayObject, index: *mut PyObject, copy_if_overlap: c_int, extra_op: *mut PyArrayObject) -> *mut PyObject]; + impl_api![301; ..=1.26; PyArray_MapIterArrayCopyIfOverlap(a: *mut PyArrayObject, index: *mut PyObject, copy_if_overlap: c_int, extra_op: *mut PyArrayObject) -> *mut PyObject]; 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]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![307; NpyDatetime_ConvertDatetime64ToDatetimeStruct(meta: *mut PyArray_DatetimeMetaData, dt: npy_datetime, out: *mut npy_datetimestruct) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![308; NpyDatetime_ConvertDatetimeStructToDatetime64(meta: *mut PyArray_DatetimeMetaData, dts: *const npy_datetimestruct, out: *mut npy_datetime) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![309; NpyDatetime_ConvertPyDateTimeToDatetimeStruct(obj: *mut PyObject, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, apply_tzinfo: c_int) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![310; NpyDatetime_GetDatetimeISO8601StrLen(local: c_int, base: NPY_DATETIMEUNIT) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - 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]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - 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]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - 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]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![314; NpyString_pack(out: *mut npy_packed_static_string) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![315; NpyString_pack_null(allocator: *mut npy_string_allocator, packed_string: *mut npy_packed_static_string) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![316; NpyString_acquire_allocator(descr: *const PyArray_StringDTypeObject) -> *mut npy_string_allocator]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![317; NpyString_acquire_allocators(n_descriptors: usize, descrs: *const *mut PyArray_Descr, allocators: *mut *mut npy_string_allocator)]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![318; NpyString_release_allocator(allocator: *mut npy_string_allocator)]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![319; NpyString_release_allocators(length: usize, allocators: *mut *mut npy_string_allocator)]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![361; PyArray_GetDefaultDescr(DType: *mut PyArray_DTypeMeta) -> *mut PyArray_Descr]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![362; PyArrayInitDTypeMeta_FromSpec(DType: *mut PyArray_DTypeMeta, spec: *mut PyArrayDTypeMeta_Spec) -> c_int]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![363; PyArray_CommonDType(dtype1: *mut PyArray_DTypeMeta, dtype2: *mut PyArray_DTypeMeta) -> PyArray_DTypeMeta]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![364; PyArray_PromoteDTypeSequence(length: npy_intp, dtypes_in: *mut *mut PyArray_DTypeMeta) -> *mut PyArray_DTypeMeta]; - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - impl_api![365; _PyDataType_GetArrFuncs(descr: *const PyArray_Descr) -> *mut PyArray_ArrFuncs]; + impl_api![307; 2.0..; NpyDatetime_ConvertDatetime64ToDatetimeStruct(meta: *mut PyArray_DatetimeMetaData, dt: npy_datetime, out: *mut npy_datetimestruct) -> c_int]; + impl_api![308; 2.0..; NpyDatetime_ConvertDatetimeStructToDatetime64(meta: *mut PyArray_DatetimeMetaData, dts: *const npy_datetimestruct, out: *mut npy_datetime) -> c_int]; + impl_api![309; 2.0..; NpyDatetime_ConvertPyDateTimeToDatetimeStruct(obj: *mut PyObject, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, apply_tzinfo: c_int) -> c_int]; + impl_api![310; 2.0..; NpyDatetime_GetDatetimeISO8601StrLen(local: c_int, base: NPY_DATETIMEUNIT) -> c_int]; + impl_api![311; 2.0..; 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; 2.0..; 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; 2.0..; 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; 2.0..; NpyString_pack(out: *mut npy_packed_static_string) -> c_int]; + impl_api![315; 2.0..; NpyString_pack_null(allocator: *mut npy_string_allocator, packed_string: *mut npy_packed_static_string) -> c_int]; + impl_api![316; 2.0..; NpyString_acquire_allocator(descr: *const PyArray_StringDTypeObject) -> *mut npy_string_allocator]; + impl_api![317; 2.0..; NpyString_acquire_allocators(n_descriptors: usize, descrs: *const *mut PyArray_Descr, allocators: *mut *mut npy_string_allocator)]; + impl_api![318; 2.0..; NpyString_release_allocator(allocator: *mut npy_string_allocator)]; + impl_api![319; 2.0..; NpyString_release_allocators(length: usize, allocators: *mut *mut npy_string_allocator)]; + impl_api![361; 2.0..; PyArray_GetDefaultDescr(DType: *mut PyArray_DTypeMeta) -> *mut PyArray_Descr]; + impl_api![362; 2.0..; PyArrayInitDTypeMeta_FromSpec(DType: *mut PyArray_DTypeMeta, spec: *mut PyArrayDTypeMeta_Spec) -> c_int]; + impl_api![363; 2.0..; PyArray_CommonDType(dtype1: *mut PyArray_DTypeMeta, dtype2: *mut PyArray_DTypeMeta) -> PyArray_DTypeMeta]; + impl_api![364; 2.0..; PyArray_PromoteDTypeSequence(length: npy_intp, dtypes_in: *mut *mut PyArray_DTypeMeta) -> *mut PyArray_DTypeMeta]; + impl_api![365; 2.0..; _PyDataType_GetArrFuncs(descr: *const PyArray_Descr) -> *mut PyArray_ArrFuncs]; - #[cfg(all(feature = "numpy-1", feature = "numpy-2"))] #[allow(non_snake_case)] pub unsafe fn PyArray_CopyInto<'py>( &self, @@ -401,10 +342,8 @@ impl PyArrayAPI { dst: *mut PyArrayObject, src: *mut PyArrayObject, ) -> c_int { - let (_, api_version) = *ABI_API_VERSIONS - .get() - .expect("ABI_API_VERSIONS is initialized"); - let offset = if api_version < NPY_2_0_API_VERSION { + let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); + let offset = if api_version < API_VERSION_2_0 { 82 } else { 50 @@ -414,7 +353,6 @@ impl PyArrayAPI { (*fptr)(dst, src) } - #[cfg(all(feature = "numpy-1", feature = "numpy-2"))] #[allow(non_snake_case)] pub unsafe fn PyArray_CastAnyTo<'py>( &self, @@ -422,10 +360,8 @@ impl PyArrayAPI { out: *mut PyArrayObject, mp: *mut PyArrayObject, ) -> c_int { - let (_, api_version) = *ABI_API_VERSIONS - .get() - .expect("ABI_API_VERSIONS is initialized"); - let offset = if api_version < NPY_2_0_API_VERSION { + let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); + let offset = if api_version < API_VERSION_2_0 { 83 } else { 51 diff --git a/src/npyffi/flags.rs b/src/npyffi/flags.rs index ba61d152..f39d5cc1 100644 --- a/src/npyffi/flags.rs +++ b/src/npyffi/flags.rs @@ -63,24 +63,19 @@ 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; -#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] -pub use super::npy_char as FlagType; -#[cfg(feature = "numpy-2")] -pub use u64 as FlagType; - -pub const NPY_ITEM_REFCOUNT: FlagType = 0x01; -pub const NPY_ITEM_HASOBJECT: FlagType = 0x01; -pub const NPY_LIST_PICKLE: FlagType = 0x02; -pub const NPY_ITEM_IS_POINTER: FlagType = 0x04; -pub const NPY_NEEDS_INIT: FlagType = 0x08; -pub const NPY_NEEDS_PYAPI: FlagType = 0x10; -pub const NPY_USE_GETITEM: FlagType = 0x20; -pub const NPY_USE_SETITEM: FlagType = 0x40; +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: FlagType = 0x80; -pub const NPY_FROM_FIELDS: FlagType = +pub const NPY_ALIGNED_STRUCT: u64 = 0x80; +pub const NPY_FROM_FIELDS: u64 = NPY_NEEDS_INIT | NPY_LIST_PICKLE | NPY_ITEM_REFCOUNT | NPY_NEEDS_PYAPI; -pub const NPY_OBJECT_DTYPE_FLAGS: FlagType = NPY_LIST_PICKLE +pub const NPY_OBJECT_DTYPE_FLAGS: u64 = 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 b4a27607..cb23c0eb 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -10,20 +10,17 @@ )] use std::mem::forget; -use std::os::raw::c_uint; -use std::os::raw::c_void; +use std::os::raw::{c_uint, c_void}; use pyo3::{ + sync::GILOnceCell, types::{PyAnyMethods, PyCapsule, PyCapsuleMethods, PyModule}, PyResult, Python, }; -#[cfg(not(any(feature = "numpy-1", feature = "numpy-2")))] -compile_error!("at least one of feature \"numpy-1\" and feature \"numpy-2\" must be enabled"); +pub const API_VERSION_2_0: c_uint = 0x00000012; -pub const NPY_2_0_API_VERSION: c_uint = 0x00000012; - -pub static ABI_API_VERSIONS: std::sync::OnceLock<(c_uint, c_uint)> = std::sync::OnceLock::new(); +pub static API_VERSION: GILOnceCell = GILOnceCell::new(); fn get_numpy_api<'py>( py: Python<'py>, @@ -39,49 +36,74 @@ fn get_numpy_api<'py>( // so we can safely cache a pointer into its interior. forget(capsule); - ABI_API_VERSIONS.get_or_init(|| { - let abi_version = unsafe { - #[allow(non_snake_case)] - let PyArray_GetNDArrayCVersion = api.offset(0) as *const extern fn () -> c_uint; - (*PyArray_GetNDArrayCVersion)() - }; - let api_version = unsafe { - #[allow(non_snake_case)] - let PyArray_GetNDArrayCFeatureVersion = api.offset(211) as *const extern fn () -> c_uint; - (*PyArray_GetNDArrayCFeatureVersion)() - }; - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - if api_version >= NPY_2_0_API_VERSION { - panic!( - "the extension was compiled for numpy 1.x but the runtime version is 2.x (ABI {:08x}.{:08x})", - abi_version, - api_version - ); - } - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] - if api_version < NPY_2_0_API_VERSION { - panic!( - "the extension was compiled for numpy 2.x but the runtime version is 1.x (ABI {:08x}.{:08x})", - abi_version, - api_version - ); - } - (abi_version, api_version) + API_VERSION.get_or_init(py, || unsafe { + #[allow(non_snake_case)] + let PyArray_GetNDArrayCFeatureVersion = api.offset(211) as *const extern "C" fn() -> c_uint; + (*PyArray_GetNDArrayCFeatureVersion)() }); Ok(api) } +const fn api_version_to_numpy_version_range(api_version: c_uint) -> (&'static str, &'static str) { + match api_version { + ..=0x00000008 => ("?", "1.7"), + 0x00000009 => ("1.8", "1.9"), + 0x0000000A => ("1.10", "1.12"), + 0x0000000B => ("1.13", "1.13"), + 0x0000000C => ("1.14", "1.15"), + 0x0000000D => ("1.16", "1.19"), + 0x0000000E => ("1.20", "1.21"), + 0x0000000F => ("1.22", "1.22"), + 0x00000010 => ("1.23", "1.24"), + 0x00000011 => ("1.25", "1.26"), + 0x00000012.. => ("2.0", "?"), + } +} + // Implements wrappers for NumPy's Array and UFunc API macro_rules! impl_api { - [$offset: expr; $fname: ident ( $($arg: ident : $t: ty),* $(,)?) $( -> $ret: ty )* ] => { + // API available on all versions + [$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 fn ($($arg : $t), *) $(-> $ret)*; + (*fptr)($($arg), *) + } + }; + + // API with version constraints, checked at runtime + [$offset: expr; ..=1.26; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { + impl_api![$offset; ..=0x00000011; $fname($($arg : $t), *) $(-> $ret)*]; + }; + [$offset: expr; 2.0..; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { + impl_api![$offset; 0x00000012..; $fname($($arg : $t), *) $(-> $ret)*]; + }; + [$offset: expr; $($minimum: literal)?..=$($maximum: literal)?; $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 fn ($($arg : $t), *) $( -> $ret )*; + pub unsafe fn $fname<'py>(&self, py: Python<'py>, $($arg : $t), *) $(-> $ret)* { + let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); + $(if api_version < $minimum { panic!( + "{} requires API {:08X} or greater (NumPy {} or greater) but the runtime version is API {:08X}", + stringify!($fname), + $minimum, + api_version_to_numpy_version_range($minimum).0, + api_version, + ) } )? + $(if api_version > $maximum { panic!( + "{} requires API {:08X} or lower (NumPy {} or lower) but the runtime version is API {:08X}", + stringify!($fname), + $maximum, + api_version_to_numpy_version_range($maximum).1, + api_version, + ) } )? + let fptr = self.get(py, $offset) as *const extern fn ($($arg: $t), *) $(-> $ret)*; (*fptr)($($arg), *) } }; + [$offset: expr; $($minimum: literal)?..; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { + impl_api![$offset; $($minimum)?..=; $fname($($arg : $t), *) $(-> $ret)*]; + }; } pub mod array; diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index 15b95a5c..980b4081 100644 --- a/src/npyffi/objects.rs +++ b/src/npyffi/objects.rs @@ -8,8 +8,6 @@ use pyo3::ffi::*; use std::os::raw::*; use super::types::*; - -#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] use crate::npyffi::*; #[repr(C)] @@ -26,7 +24,6 @@ pub struct PyArrayObject { pub weakreflist: *mut PyObject, } -#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] #[repr(C)] #[derive(Copy, Clone)] pub struct PyArray_Descr { @@ -100,52 +97,21 @@ struct _PyArray_LegacyDescr { pub c_metadata: *mut NpyAuxData, } -#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] -pub use PyArray_DescrProto as PyArray_Descr; - -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] -pub use _PyArray_DescrNumPy2 as PyArray_Descr; - -#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] -#[allow(non_snake_case)] -#[inline(always)] -pub fn PyDataType_ISLEGACY(_dtype: *const PyArray_Descr) -> bool { - true -} - -#[cfg(feature = "numpy-2")] -#[allow(non_snake_case)] -#[inline(always)] -pub fn PyDataType_ISLEGACY(dtype: *const PyArray_Descr) -> bool { - unsafe { (*dtype).type_num < NPY_TYPES::NPY_VSTRING as i32 && (*dtype).type_num >= 0 } -} - -#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] #[allow(non_snake_case)] #[inline(always)] -pub fn PyDataType_SET_ELSIZE(dtype: *mut PyArray_Descr, size: npy_intp) { - unsafe { - (*dtype).elsize = size as c_int; - } -} - -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] -#[allow(non_snake_case)] -#[inline(always)] -pub fn PyDataType_SET_ELSIZE(dtype: *mut PyArray_Descr, size: npy_intp) { - unsafe { - (*dtype).elsize = size; - } +pub unsafe fn PyDataType_ISLEGACY(dtype: *const PyArray_Descr) -> bool { + (*dtype).type_num < NPY_TYPES::NPY_VSTRING as i32 && (*dtype).type_num >= 0 } -#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] #[allow(non_snake_case)] #[inline(always)] -pub fn PyDataType_SET_ELSIZE(dtype: *mut PyArray_Descr, size: npy_intp) { - let (_, api_version) = *ABI_API_VERSIONS - .get() - .expect("ABI_API_VERSIONS is initialized"); - if api_version < NPY_2_0_API_VERSION { +pub unsafe fn PyDataType_SET_ELSIZE<'py>( + py: Python<'py>, + dtype: *mut PyArray_Descr, + size: npy_intp, +) { + let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); + if api_version < API_VERSION_2_0 { unsafe { (*(dtype as *mut PyArray_DescrProto)).elsize = size as c_int; } @@ -156,73 +122,27 @@ pub fn PyDataType_SET_ELSIZE(dtype: *mut PyArray_Descr, size: npy_intp) { } } -#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] -#[allow(non_snake_case)] -#[inline(always)] -pub fn PyDataType_FLAGS(dtype: *const PyArray_Descr) -> npy_uint64 { - unsafe { (*dtype).flags as c_uchar as npy_uint64 } -} - -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] -#[allow(non_snake_case)] -#[inline(always)] -pub fn PyDataType_FLAGS(dtype: *const PyArray_Descr) -> npy_uint64 { - unsafe { (*dtype).flags } -} - -#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] #[allow(non_snake_case)] #[inline(always)] -pub fn PyDataType_FLAGS(dtype: *const PyArray_Descr) -> npy_uint64 { - let (_, api_version) = *ABI_API_VERSIONS - .get() - .expect("ABI_API_VERSIONS is initialized"); - if api_version < NPY_2_0_API_VERSION { +pub unsafe fn PyDataType_FLAGS<'py>(py: Python<'py>, dtype: *const PyArray_Descr) -> npy_uint64 { + let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); + if api_version < API_VERSION_2_0 { unsafe { (*(dtype as *mut PyArray_DescrProto)).flags as c_uchar as npy_uint64 } } else { unsafe { (*(dtype as *mut _PyArray_DescrNumPy2)).flags } } } -#[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] -macro_rules! DESCR_ACCESSOR { - ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $zero:expr) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $name(dtype: *const PyArray_Descr) -> $type { - unsafe { (*dtype).$property as $type } - } - }; -} - -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] -macro_rules! DESCR_ACCESSOR { - ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $zero:expr) => { - #[allow(non_snake_case)] - #[inline(always)] - pub fn $name(dtype: *const PyArray_Descr) -> $type { - if $legacy_only && !PyDataType_ISLEGACY(dtype) { - $zero - } else { - unsafe { (*(dtype as *const _PyArray_LegacyDescr)).$property } - } - } - }; -} - -#[cfg(all(feature = "numpy-1", feature = "numpy-2"))] -macro_rules! DESCR_ACCESSOR { +macro_rules! define_descr_accessor { ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $zero:expr) => { #[allow(non_snake_case)] #[inline(always)] - pub fn $name(dtype: *const PyArray_Descr) -> $type { + pub unsafe fn $name<'py>(py: Python<'py>, dtype: *const PyArray_Descr) -> $type { if $legacy_only && !PyDataType_ISLEGACY(dtype) { $zero } else { - let (_, api_version) = *ABI_API_VERSIONS - .get() - .expect("ABI_API_VERSIONS is initialized"); - if api_version < NPY_2_0_API_VERSION { + let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); + if api_version < API_VERSION_2_0 { unsafe { (*(dtype as *mut PyArray_DescrProto)).$property as $type } } else { unsafe { (*(dtype as *const _PyArray_LegacyDescr)).$property } @@ -232,37 +152,37 @@ macro_rules! DESCR_ACCESSOR { }; } -DESCR_ACCESSOR!(PyDataType_ELSIZE, elsize, npy_intp, false, 0); -DESCR_ACCESSOR!(PyDataType_ALIGNMENT, alignment, npy_intp, false, 0); -DESCR_ACCESSOR!( +define_descr_accessor!(PyDataType_ELSIZE, elsize, npy_intp, false, 0); +define_descr_accessor!(PyDataType_ALIGNMENT, alignment, npy_intp, false, 0); +define_descr_accessor!( PyDataType_METADATA, metadata, *mut PyObject, true, std::ptr::null_mut() ); -DESCR_ACCESSOR!( +define_descr_accessor!( PyDataType_SUBARRAY, subarray, *mut PyArray_ArrayDescr, true, std::ptr::null_mut() ); -DESCR_ACCESSOR!( +define_descr_accessor!( PyDataType_NAMES, names, *mut PyObject, true, std::ptr::null_mut() ); -DESCR_ACCESSOR!( +define_descr_accessor!( PyDataType_FIELDS, fields, *mut PyObject, true, std::ptr::null_mut() ); -DESCR_ACCESSOR!( +define_descr_accessor!( PyDataType_C_METADATA, c_metadata, *mut NpyAuxData, @@ -645,15 +565,10 @@ pub struct PyArray_DatetimeDTypeMetaData { // npy_packed_static_string and npy_string_allocator are opaque pointers // consider extern types when they are stabilized // https://github.com/rust-lang/rust/issues/43467 -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] pub type npy_packed_static_string = c_void; -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] pub type npy_string_allocator = c_void; - -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] pub type PyArray_DTypeMeta = PyTypeObject; -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] #[repr(C)] #[derive(Clone, Copy)] pub struct npy_static_string { @@ -661,7 +576,6 @@ pub struct npy_static_string { buf: *const c_char, } -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] #[repr(C)] #[derive(Clone, Copy)] pub struct PyArray_StringDTypeObject { @@ -676,7 +590,6 @@ pub struct PyArray_StringDTypeObject { pub allocator: *mut npy_string_allocator, } -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] #[repr(C)] #[derive(Clone, Copy)] pub struct PyArrayMethod_Spec { @@ -689,7 +602,6 @@ pub struct PyArrayMethod_Spec { pub slots: *mut PyType_Slot, } -#[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] #[repr(C)] #[derive(Clone, Copy)] pub struct PyArrayDTypeMeta_Spec { diff --git a/src/npyffi/types.rs b/src/npyffi/types.rs index a0a0203c..68df9c52 100644 --- a/src/npyffi/types.rs +++ b/src/npyffi/types.rs @@ -141,15 +141,10 @@ pub enum NPY_TYPES { NPY_DATETIME = 21, NPY_TIMEDELTA = 22, NPY_HALF = 23, - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] - NPY_NTYPES = 24, - #[cfg(all(not(feature = "numpy-1"), feature = "numpy-2"))] NPY_NTYPES_LEGACY = 24, NPY_NOTYPE = 25, - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] NPY_CHAR = 26, NPY_USERDEF = 256, - #[cfg(feature = "numpy-2")] NPY_VSTRING = 2056, } diff --git a/src/strings.rs b/src/strings.rs index 66cc0b15..1f440fbd 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -184,7 +184,7 @@ impl TypeDescriptors { let dtype = PyArrayDescr::new_from_npy_type(py, npy_type); let descr = &mut *dtype.as_dtype_ptr(); - PyDataType_SET_ELSIZE(descr, size.try_into().unwrap()); + PyDataType_SET_ELSIZE(py, descr, size.try_into().unwrap()); descr.byteorder = byteorder; entry.insert(dtype.into()) From 8619b761ce277a0af8cc46990f7b835998d5baaa Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Sat, 8 Jun 2024 11:11:23 +1000 Subject: [PATCH 06/11] Avoid API changes by using self.py() --- src/dtype.rs | 165 ++++++++++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 75 deletions(-) diff --git a/src/dtype.rs b/src/dtype.rs index 17a6b747..2ee0695e 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -204,8 +204,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.itemsize`][dtype-itemsize]. /// /// [dtype-itemsiize]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.itemsize.html - pub fn itemsize<'py>(&self, py: Python<'py>) -> usize { - self.as_borrowed().itemsize(py) + pub fn itemsize(&self) -> usize { + self.as_borrowed().itemsize() } /// Returns the required alignment (bytes) of this type descriptor according to the compiler. @@ -213,8 +213,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.alignment`][dtype-alignment]. /// /// [dtype-alignment]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.alignment.html - pub fn alignment<'py>(&self, py: Python<'py>) -> usize { - self.as_borrowed().alignment(py) + pub fn alignment(&self) -> usize { + self.as_borrowed().alignment() } /// Returns an ASCII character indicating the byte-order of this type descriptor object. @@ -255,8 +255,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.flags`][dtype-flags]. /// /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html - pub fn flags<'py>(&self, py: Python<'py>) -> u64 { - self.as_borrowed().flags(py) + pub fn flags<'py>(&self) -> u64 { + self.as_borrowed().flags() } /// Returns the number of dimensions if this type descriptor represents a sub-array, and zero otherwise. @@ -264,8 +264,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.ndim`][dtype-ndim]. /// /// [dtype-ndim]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.ndim.html - pub fn ndim<'py>(&self, py: Python<'py>) -> usize { - self.as_borrowed().ndim(py) + pub fn ndim<'py>(&self) -> usize { + self.as_borrowed().ndim() } /// Returns the type descriptor for the base element of subarrays, regardless of their dimension or shape. @@ -295,8 +295,8 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.hasobject`][dtype-hasobject]. /// /// [dtype-hasobject]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.hasobject.html - pub fn has_object<'py>(&self, py: Python<'py>) -> bool { - self.as_borrowed().has_object(py) + pub fn has_object(&self) -> bool { + self.as_borrowed().has_object() } /// Returns true if the type descriptor is a struct which maintains field alignment. @@ -307,18 +307,18 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.isalignedstruct`][dtype-isalignedstruct]. /// /// [dtype-isalignedstruct]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.isalignedstruct.html - pub fn is_aligned_struct<'py>(&self, py: Python<'py>) -> bool { - self.as_borrowed().is_aligned_struct(py) + pub fn is_aligned_struct(&self) -> bool { + self.as_borrowed().is_aligned_struct() } /// Returns true if the type descriptor is a sub-array. - pub fn has_subarray<'py>(&self, py: Python<'py>) -> bool { - self.as_borrowed().has_subarray(py) + pub fn has_subarray(&self) -> bool { + self.as_borrowed().has_subarray() } /// Returns true if the type descriptor is a structured type. - pub fn has_fields<'py>(&self, py: Python<'py>) -> bool { - self.as_borrowed().has_fields(py) + pub fn has_fields(&self) -> bool { + self.as_borrowed().has_fields() } /// Returns true if type descriptor byteorder is native, or `None` if not applicable. @@ -394,19 +394,14 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.itemsize`][dtype-itemsize]. /// /// [dtype-itemsiize]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.itemsize.html - - fn itemsize(&self, py: Python<'py>) -> usize { - unsafe { PyDataType_ELSIZE(py, self.as_dtype_ptr()).max(0) as _ } - } + fn itemsize(&self) -> usize; /// Returns the required alignment (bytes) of this type descriptor according to the compiler. /// /// Equivalent to [`numpy.dtype.alignment`][dtype-alignment]. /// /// [dtype-alignment]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.alignment.html - fn alignment(&self, py: Python<'py>) -> usize { - unsafe { PyDataType_ALIGNMENT(py, self.as_dtype_ptr()).max(0) as _ } - } + fn alignment(&self) -> usize; /// Returns an ASCII character indicating the byte-order of this type descriptor object. /// @@ -446,21 +441,14 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.flags`][dtype-flags]. /// /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html - fn flags(&self, py: Python<'py>) -> u64 { - unsafe { PyDataType_FLAGS(py, self.as_dtype_ptr()) } - } + fn flags(&self) -> u64; /// Returns the number of dimensions if this type descriptor represents a sub-array, and zero otherwise. /// /// Equivalent to [`numpy.dtype.ndim`][dtype-ndim]. /// /// [dtype-ndim]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.ndim.html - fn ndim(&self, py: Python<'py>) -> usize { - if !self.has_subarray(py) { - return 0; - } - unsafe { PyTuple_Size((*PyDataType_SUBARRAY(py, self.as_dtype_ptr())).shape).max(0) as _ } - } + fn ndim(&self) -> usize; /// Returns the type descriptor for the base element of subarrays, regardless of their dimension or shape. /// @@ -485,8 +473,8 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.hasobject`][dtype-hasobject]. /// /// [dtype-hasobject]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.hasobject.html - fn has_object(&self, py: Python<'py>) -> bool { - self.flags(py) & NPY_ITEM_HASOBJECT != 0 + fn has_object(&self) -> bool { + self.flags() & NPY_ITEM_HASOBJECT != 0 } /// Returns true if the type descriptor is a struct which maintains field alignment. @@ -497,21 +485,19 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Equivalent to [`numpy.dtype.isalignedstruct`][dtype-isalignedstruct]. /// /// [dtype-isalignedstruct]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.isalignedstruct.html - fn is_aligned_struct(&self, py: Python<'py>) -> bool { - self.flags(py) & NPY_ALIGNED_STRUCT != 0 + fn is_aligned_struct(&self) -> bool { + self.flags() & NPY_ALIGNED_STRUCT != 0 } /// Returns true if the type descriptor is a sub-array. - fn has_subarray(&self, py: Python<'py>) -> bool { - // equivalent to PyDataType_HASSUBARRAY(self) - unsafe { !PyDataType_SUBARRAY(py, self.as_dtype_ptr()).is_null() } - } + /// + /// Equivalent to PyDataType_HASSUBARRAY(self) + fn has_subarray(&self) -> bool; /// Returns true if the type descriptor is a structured type. - fn has_fields(&self, py: Python<'py>) -> bool { - // equivalent to PyDataType_HASFIELDS(self) - unsafe { !PyDataType_NAMES(py, self.as_dtype_ptr()).is_null() } - } + /// + /// Equivalent to PyDataType_HASFIELDS(self). + fn has_fields(&self) -> bool; /// Returns true if type descriptor byteorder is native, or `None` if not applicable. fn is_native_byteorder(&self) -> Option { @@ -575,8 +561,29 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { unsafe { PyType::from_borrowed_type_ptr(self.py(), dtype_type_ptr) } } + fn itemsize(&self) -> usize { + unsafe { PyDataType_ELSIZE(self.py(), self.as_dtype_ptr()).max(0) as _ } + } + + fn alignment(&self) -> usize { + unsafe { PyDataType_ALIGNMENT(self.py(), self.as_dtype_ptr()).max(0) as _ } + } + + fn flags(&self) -> u64 { + unsafe { PyDataType_FLAGS(self.py(), self.as_dtype_ptr()) } + } + + fn ndim(&self) -> usize { + if !self.has_subarray() { + return 0; + } + unsafe { + PyTuple_Size((*PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr())).shape).max(0) as _ + } + } + fn base(&self) -> Bound<'py, PyArrayDescr> { - if !self.has_subarray(self.py()) { + if !self.has_subarray() { self.clone() } else { unsafe { @@ -592,7 +599,7 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { } fn shape(&self) -> Vec { - if !self.has_subarray(self.py()) { + if !self.has_subarray() { Vec::new() } else { // NumPy guarantees that shape is a tuple of non-negative integers so this should never panic. @@ -607,8 +614,16 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { } } + fn has_subarray(&self) -> bool { + unsafe { !PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr()).is_null() } + } + + fn has_fields(&self) -> bool { + unsafe { !PyDataType_NAMES(self.py(), self.as_dtype_ptr()).is_null() } + } + fn names(&self) -> Option> { - if !self.has_fields(self.py()) { + if !self.has_fields() { return None; } let names = unsafe { @@ -618,7 +633,7 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { } fn get_field(&self, name: &str) -> PyResult<(Bound<'py, PyArrayDescr>, usize)> { - if !self.has_fields(self.py()) { + if !self.has_fields() { return Err(PyValueError::new_err( "cannot get field information: type descriptor has no fields", )); @@ -830,7 +845,7 @@ mod tests { let dt = PyArrayDescr::new_bound(py, [("a", "O"), ("b", "?")].as_ref()).unwrap(); assert_eq!(dt.names(), Some(vec!["a".to_owned(), "b".to_owned()])); - assert!(dt.has_object(py)); + assert!(dt.has_object()); assert!(dt .get_field("a") .unwrap() @@ -884,21 +899,21 @@ mod tests { let dt = dtype_bound::(py); assert_eq!(dt.num(), NPY_TYPES::NPY_DOUBLE as c_int); - assert_eq!(dt.flags(py), 0); + assert_eq!(dt.flags(), 0); assert_eq!(dt.typeobj().qualname().unwrap(), "float64"); assert_eq!(dt.char(), b'd'); assert_eq!(dt.kind(), b'f'); assert_eq!(dt.byteorder(), b'='); assert_eq!(dt.is_native_byteorder(), Some(true)); - assert_eq!(dt.itemsize(py), 8); - assert_eq!(dt.alignment(py), 8); - assert!(!dt.has_object(py)); + assert_eq!(dt.itemsize(), 8); + assert_eq!(dt.alignment(), 8); + assert!(!dt.has_object()); assert!(dt.names().is_none()); - assert!(!dt.has_fields(py)); - assert!(!dt.is_aligned_struct(py)); - assert!(!dt.has_subarray(py)); + assert!(!dt.has_fields()); + assert!(!dt.is_aligned_struct()); + assert!(!dt.has_subarray()); assert!(dt.base().is_equiv_to(&dt)); - assert_eq!(dt.ndim(py), 0); + assert_eq!(dt.ndim(), 0); assert_eq!(dt.shape(), vec![]); }); } @@ -920,20 +935,20 @@ mod tests { .unwrap(); assert_eq!(dt.num(), NPY_TYPES::NPY_VOID as c_int); - assert_eq!(dt.flags(py), 0); + assert_eq!(dt.flags(), 0); assert_eq!(dt.typeobj().qualname().unwrap(), "void"); assert_eq!(dt.char(), b'V'); assert_eq!(dt.kind(), b'V'); assert_eq!(dt.byteorder(), b'|'); assert_eq!(dt.is_native_byteorder(), None); - assert_eq!(dt.itemsize(py), 48); - assert_eq!(dt.alignment(py), 8); - assert!(!dt.has_object(py)); + assert_eq!(dt.itemsize(), 48); + assert_eq!(dt.alignment(), 8); + assert!(!dt.has_object()); assert!(dt.names().is_none()); - assert!(!dt.has_fields(py)); - assert!(!dt.is_aligned_struct(py)); - assert!(dt.has_subarray(py)); - assert_eq!(dt.ndim(py), 2); + assert!(!dt.has_fields()); + assert!(!dt.is_aligned_struct()); + assert!(dt.has_subarray()); + assert_eq!(dt.ndim(), 2); assert_eq!(dt.shape(), vec![2, 3]); assert!(dt.base().is_equiv_to(&dtype_bound::(py))); }); @@ -956,25 +971,25 @@ mod tests { .unwrap(); assert_eq!(dt.num(), NPY_TYPES::NPY_VOID as c_int); - assert_ne!(dt.flags(py) & NPY_ITEM_HASOBJECT, 0); - assert_ne!(dt.flags(py) & NPY_NEEDS_PYAPI, 0); - assert_ne!(dt.flags(py) & NPY_ALIGNED_STRUCT, 0); + assert_ne!(dt.flags() & NPY_ITEM_HASOBJECT, 0); + assert_ne!(dt.flags() & NPY_NEEDS_PYAPI, 0); + assert_ne!(dt.flags() & NPY_ALIGNED_STRUCT, 0); assert_eq!(dt.typeobj().qualname().unwrap(), "void"); assert_eq!(dt.char(), b'V'); assert_eq!(dt.kind(), b'V'); assert_eq!(dt.byteorder(), b'|'); assert_eq!(dt.is_native_byteorder(), None); - assert_eq!(dt.itemsize(py), 24); - assert_eq!(dt.alignment(py), 8); - assert!(dt.has_object(py)); + assert_eq!(dt.itemsize(), 24); + assert_eq!(dt.alignment(), 8); + assert!(dt.has_object()); assert_eq!( dt.names(), Some(vec!["x".to_owned(), "y".to_owned(), "z".to_owned()]) ); - assert!(dt.has_fields(py)); - assert!(dt.is_aligned_struct(py)); - assert!(!dt.has_subarray(py)); - assert_eq!(dt.ndim(py), 0); + assert!(dt.has_fields()); + assert!(dt.is_aligned_struct()); + assert!(!dt.has_subarray()); + assert_eq!(dt.ndim(), 0); assert_eq!(dt.shape(), vec![]); assert!(dt.base().is_equiv_to(&dt)); let x = dt.get_field("x").unwrap(); From bea69cdae922f1e3ac43d0adf884dfa0a5acc8c2 Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Sat, 8 Jun 2024 11:18:59 +1000 Subject: [PATCH 07/11] Fixup --- src/dtype.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dtype.rs b/src/dtype.rs index 2ee0695e..be684262 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -255,7 +255,7 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.flags`][dtype-flags]. /// /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html - pub fn flags<'py>(&self) -> u64 { + pub fn flags(&self) -> u64 { self.as_borrowed().flags() } @@ -264,7 +264,7 @@ impl PyArrayDescr { /// Equivalent to [`numpy.dtype.ndim`][dtype-ndim]. /// /// [dtype-ndim]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.ndim.html - pub fn ndim<'py>(&self) -> usize { + pub fn ndim(&self) -> usize { self.as_borrowed().ndim() } From cc5918ff0051077762c4d8653e1d8b324fcf2461 Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Mon, 8 Jul 2024 11:20:46 -0600 Subject: [PATCH 08/11] Fix flags for Windows Contributed by @stinodego --- src/dtype.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dtype.rs b/src/dtype.rs index be684262..bb2dee2c 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -570,7 +570,7 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { } fn flags(&self) -> u64 { - unsafe { PyDataType_FLAGS(self.py(), self.as_dtype_ptr()) } + unsafe { PyDataType_FLAGS(self.py(), self.as_dtype_ptr()) as _ } } fn ndim(&self) -> usize { From 09d1e4985088b62cf2b94e6194b527d6d71f4841 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 21 Jul 2024 14:33:04 +0200 Subject: [PATCH 09/11] Avoid half-open range patterns to appease our MSRV build. --- src/npyffi/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/npyffi/mod.rs b/src/npyffi/mod.rs index cb23c0eb..b945b4cc 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -47,7 +47,7 @@ fn get_numpy_api<'py>( const fn api_version_to_numpy_version_range(api_version: c_uint) -> (&'static str, &'static str) { match api_version { - ..=0x00000008 => ("?", "1.7"), + api_version if api_version <= 0x00000008 => ("?", "1.7"), 0x00000009 => ("1.8", "1.9"), 0x0000000A => ("1.10", "1.12"), 0x0000000B => ("1.13", "1.13"), @@ -57,7 +57,7 @@ const fn api_version_to_numpy_version_range(api_version: c_uint) -> (&'static st 0x0000000F => ("1.22", "1.22"), 0x00000010 => ("1.23", "1.24"), 0x00000011 => ("1.25", "1.26"), - 0x00000012.. => ("2.0", "?"), + api_version if api_version >= 0x00000012 => ("2.0", "?"), } } From 247226989affb6866563787e3960c112b6d237b0 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 21 Jul 2024 14:48:51 +0200 Subject: [PATCH 10/11] Remove residual numpy-1/2 feature usage. --- src/array.rs | 4 ++-- src/npyffi/objects.rs | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/array.rs b/src/array.rs index dfc63072..276c9918 100644 --- a/src/array.rs +++ b/src/array.rs @@ -40,7 +40,7 @@ use crate::untyped_array::{PyUntypedArray, PyUntypedArrayMethods}; /// # Memory location /// /// - Allocated by Rust: Constructed via [`IntoPyArray`] or -/// [`from_vec`][Self::from_vec] or [`from_owned_array`][Self::from_owned_array]. +/// [`from_vec`][Self::from_vec] or [`from_owned_array`][Self::from_owned_array]. /// /// These methods transfers ownership of the Rust allocation into a suitable Python object /// and uses the memory as the internal buffer backing the NumPy array. @@ -49,7 +49,7 @@ use crate::untyped_array::{PyUntypedArray, PyUntypedArrayMethods}; /// when used with this kind of array as NumPy cannot reallocate the internal buffer. /// /// - Allocated by NumPy: Constructed via other methods, like [`ToPyArray`] or -/// [`from_slice`][Self::from_slice] or [`from_array`][Self::from_array]. +/// [`from_slice`][Self::from_slice] or [`from_array`][Self::from_array]. /// /// These methods allocate memory in Python's private heap via NumPy's API. /// diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index 980b4081..03c00c1a 100644 --- a/src/npyffi/objects.rs +++ b/src/npyffi/objects.rs @@ -378,10 +378,7 @@ pub struct PyUFuncObject { pub type_resolver: PyUFunc_TypeResolutionFunc, pub legacy_inner_loop_selector: PyUFunc_LegacyInnerLoopSelectionFunc, pub reserved2: *mut c_void, - #[cfg(all(feature = "numpy-1", not(feature = "numpy-2")))] pub masked_inner_loop_selector: PyUFunc_MaskedInnerLoopSelectionFunc, - #[cfg(all(not(feature = "numpy-2"), feature = "numpy-2"))] - pub reserved3: *mut c_void, pub op_flags: *mut npy_uint32, pub iter_flags: npy_uint32, } From 1d33e704c2038cab03f7c4cab96c41addcaae222 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 21 Jul 2024 14:49:31 +0200 Subject: [PATCH 11/11] Use explicit patterns so exhaustiveness checking works, but avoid open bounds for the MSRV build. --- src/npyffi/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/npyffi/mod.rs b/src/npyffi/mod.rs index b945b4cc..68ccf361 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -47,7 +47,7 @@ fn get_numpy_api<'py>( const fn api_version_to_numpy_version_range(api_version: c_uint) -> (&'static str, &'static str) { match api_version { - api_version if api_version <= 0x00000008 => ("?", "1.7"), + 0..=0x00000008 => ("?", "1.7"), 0x00000009 => ("1.8", "1.9"), 0x0000000A => ("1.10", "1.12"), 0x0000000B => ("1.13", "1.13"), @@ -57,7 +57,7 @@ const fn api_version_to_numpy_version_range(api_version: c_uint) -> (&'static st 0x0000000F => ("1.22", "1.22"), 0x00000010 => ("1.23", "1.24"), 0x00000011 => ("1.25", "1.26"), - api_version if api_version >= 0x00000012 => ("2.0", "?"), + 0x00000012..=c_uint::MAX => ("2.0", "?"), } }