From 39f0b6c45f6a67674dda1795ac27aba95451309f Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Tue, 4 Jun 2024 09:53:53 +1000 Subject: [PATCH 01/22] 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 4c71108a9..0bf5f9175 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 3b6c0b70ce8d128809f01916c104639e75a197d6 Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Tue, 4 Jun 2024 09:57:18 +1000 Subject: [PATCH 02/22] 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 b62c3b22d..224b724fd 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 7c9dedb6e..ba61d152a 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 1f71f2ca5..56a76bad0 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 6b252bcba..15b95a5c4 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 e147576fd..a0a0203c9 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 c2e55f8dbbd364966150725acb49e393e76ec721 Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Tue, 4 Jun 2024 09:57:39 +1000 Subject: [PATCH 03/22] 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 1ff4344e7..439ce161e 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 9a96799e5..eb5d6c023 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 9aa37eab9..0fe5dc538 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 74606289e..66cc0b15e 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 e0607351f5caf77f8a654995990871c087fae593 Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Tue, 4 Jun 2024 17:03:18 +1000 Subject: [PATCH 04/22] 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 56a76bad0..b4a27607b 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 31924fb5e62d0532dbd11f8fa53fa7b25ed4712c Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Fri, 7 Jun 2024 20:11:34 +1000 Subject: [PATCH 05/22] 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 0bf5f9175..4c71108a9 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 439ce161e..38f8963e5 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 eb5d6c023..794170616 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 0fe5dc538..17a6b747f 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 224b724fd..2e4192fc5 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 ba61d152a..f39d5cc11 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 b4a27607b..cb23c0eba 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 15b95a5c4..980b4081a 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 a0a0203c9..68df9c52f 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 66cc0b15e..1f440fbda 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 7f59548efbd6b2635ca71fe147735a4b0962ccae Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Sat, 8 Jun 2024 11:11:23 +1000 Subject: [PATCH 06/22] 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 17a6b747f..2ee0695ec 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 9d7e0a2c29f2e25c33c6214d48848241f030552a Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Sat, 8 Jun 2024 11:18:59 +1000 Subject: [PATCH 07/22] Fixup --- src/dtype.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dtype.rs b/src/dtype.rs index 2ee0695ec..be684262c 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 3777299551424e0ade974ed31f9142b623f05667 Mon Sep 17 00:00:00 2001 From: Alexandre Marcireau Date: Mon, 8 Jul 2024 11:20:46 -0600 Subject: [PATCH 08/22] 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 be684262c..bb2dee2c1 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 c89932b1460310ae1b463b8b84a64b85cda32566 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 21 Jul 2024 14:33:04 +0200 Subject: [PATCH 09/22] 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 cb23c0eba..b945b4cc3 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 92b094795adf0d31c8905390704bad46908621c2 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 21 Jul 2024 14:48:51 +0200 Subject: [PATCH 10/22] Remove residual numpy-1/2 feature usage. --- src/npyffi/objects.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index 980b4081a..03c00c1a2 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 9de97ba270cbe9488fc37cd235be98e209cb8fa2 Mon Sep 17 00:00:00 2001 From: Adam Reichold Date: Sun, 21 Jul 2024 14:49:31 +0200 Subject: [PATCH 11/22] 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 b945b4cc3..68ccf3619 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", "?"), } } From be3ce9bc71447dfd61303ffb300eea7333efc3d4 Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Sat, 31 Aug 2024 09:32:04 -0700 Subject: [PATCH 12/22] Add ci job to test numpy2 and allow numpy2 in examples --- .github/workflows/ci.yml | 46 ++++++++++++++++++++++++++++++++---- examples/linalg/noxfile.py | 2 +- examples/parallel/noxfile.py | 2 +- examples/simple/noxfile.py | 2 +- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d78d72a6a..0ccb66803 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: shell: python - name: Test run: | - pip install "numpy<2" ml_dtypes + pip install "numpy" ml_dtypes cargo test --all-features # Not on PyPy, because no embedding API if: ${{ !startsWith(matrix.python-version, 'pypy') }} @@ -83,6 +83,44 @@ jobs: CARGO_BUILD_TARGET: ${{ matrix.platform.rust-target }} RUST_BACKTRACE: 1 + test-numpy1: + name: python${{ matrix.python-version }} numpy1 + runs-on: ubuntu-latest + needs: [lint, check-msrv, examples] + strategy: + fail-fast: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-no-fail-fast') }} + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Install toml + run: pip install toml + - name: Edit Cargo.toml and enable new resolver + run: | + import toml + cargo_toml = toml.load("Cargo.toml") + cargo_toml["package"]["resolver"] = "2" + with open("Cargo.toml", "w") as f: + toml.dump(cargo_toml, f) + shell: python + - name: Test + run: | + pip install "numpy<2" ml_dtypes + cargo test --all-features + - name: Test example + run: | + pip install nox + nox -f examples/simple/noxfile.py + env: + CARGO_TERM_VERBOSE: true + RUST_BACKTRACE: 1 + cross-build: runs-on: ubuntu-latest needs: [lint, check-msrv, examples] @@ -104,7 +142,7 @@ jobs: continue-on-error: true - uses: taiki-e/install-action@valgrind - run: | - pip install "numpy<2" ml_dtypes + pip install "numpy" ml_dtypes cargo test --all-features --release env: CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: valgrind --leak-check=no --error-exitcode=1 @@ -119,7 +157,7 @@ jobs: continue-on-error: true - uses: taiki-e/install-action@cargo-careful - run: | - pip install "numpy<2" ml_dtypes + pip install "numpy" ml_dtypes cargo careful test --all-features check-msrv: @@ -195,7 +233,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install numpy - run: pip install "numpy<2" ml_dtypes + run: pip install "numpy" ml_dtypes - uses: Swatinem/rust-cache@v2 continue-on-error: true - uses: dtolnay/rust-toolchain@stable diff --git a/examples/linalg/noxfile.py b/examples/linalg/noxfile.py index 291b0b1a2..6154c9517 100644 --- a/examples/linalg/noxfile.py +++ b/examples/linalg/noxfile.py @@ -3,6 +3,6 @@ @nox.session def tests(session): - session.install("pip", "numpy<2", "pytest") + session.install("pip", "numpy", "pytest") session.run("pip", "install", ".", "-v") session.run("pytest") diff --git a/examples/parallel/noxfile.py b/examples/parallel/noxfile.py index 291b0b1a2..6154c9517 100644 --- a/examples/parallel/noxfile.py +++ b/examples/parallel/noxfile.py @@ -3,6 +3,6 @@ @nox.session def tests(session): - session.install("pip", "numpy<2", "pytest") + session.install("pip", "numpy", "pytest") session.run("pip", "install", ".", "-v") session.run("pytest") diff --git a/examples/simple/noxfile.py b/examples/simple/noxfile.py index 291b0b1a2..6154c9517 100644 --- a/examples/simple/noxfile.py +++ b/examples/simple/noxfile.py @@ -3,6 +3,6 @@ @nox.session def tests(session): - session.install("pip", "numpy<2", "pytest") + session.install("pip", "numpy", "pytest") session.run("pip", "install", ".", "-v") session.run("pytest") From 1a93b96ed1ae6b351a7821466ecc38b902b1982f Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Sat, 31 Aug 2024 09:32:34 -0700 Subject: [PATCH 13/22] Make size and buf fields public in npy_static_string --- src/npyffi/objects.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index 03c00c1a2..7cbc4dbde 100644 --- a/src/npyffi/objects.rs +++ b/src/npyffi/objects.rs @@ -569,8 +569,8 @@ pub type PyArray_DTypeMeta = PyTypeObject; #[repr(C)] #[derive(Clone, Copy)] pub struct npy_static_string { - size: usize, - buf: *const c_char, + pub size: usize, + pub buf: *const c_char, } #[repr(C)] From 2882c6af5a80c54e1c63ea0a0b4307561fe79bd5 Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Sat, 31 Aug 2024 11:10:03 -0700 Subject: [PATCH 14/22] Fixes from @adamreichold review --- src/dtype.rs | 47 +++++++++++++++++-------------------------- src/npyffi/objects.rs | 20 +++++++++--------- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/dtype.rs b/src/dtype.rs index bb2dee2c1..c3e924d11 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -254,7 +254,10 @@ impl PyArrayDescr { /// /// Equivalent to [`numpy.dtype.flags`][dtype-flags]. /// + /// In numpy 2 the flags field was widened to allow for more flags. + /// /// [dtype-flags]: https://numpy.org/doc/stable/reference/generated/numpy.dtype.flags.html + /// [dtype-changes]: https://numpy.org/devdocs/numpy_2_0_migration_guide.html#the-pyarray-descr-struct-has-been-changed pub fn flags(&self) -> u64 { self.as_borrowed().flags() } @@ -491,7 +494,7 @@ pub trait PyArrayDescrMethods<'py>: Sealed { /// Returns true if the type descriptor is a sub-array. /// - /// Equivalent to PyDataType_HASSUBARRAY(self) + /// Equivalent to PyDataType_HASSUBARRAY(self). fn has_subarray(&self) -> bool; /// Returns true if the type descriptor is a structured type. @@ -574,43 +577,31 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { } 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 _ + let subarray = unsafe { PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr()).as_ref() }; + match subarray { + None => 0, + Some(subarray) => unsafe { PyTuple_Size(subarray.shape) }.max(0) as _ } } fn base(&self) -> Bound<'py, PyArrayDescr> { - if !self.has_subarray() { - self.clone() - } else { - unsafe { - Bound::from_borrowed_ptr( - self.py(), - (*PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr())) - .base - .cast(), - ) - .downcast_into_unchecked() + let subarray = unsafe { PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr()).as_ref() }; + match subarray { + None => self.clone(), + Some(subarray) => unsafe { + Bound::from_borrowed_ptr(self.py(), subarray.base.cast()).downcast_into_unchecked() } } } fn shape(&self) -> Vec { - if !self.has_subarray() { - 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.py(), self.as_dtype_ptr())).shape, - ) + let subarray = unsafe { PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr()).as_ref() }; + match subarray { + None => Vec::new(), + Some(subarray) => { + // NumPy guarantees that shape is a tuple of non-negative integers so this should never panic. + unsafe { Borrowed::from_ptr(self.py(), subarray.shape) }.extract().unwrap() } - .extract() - .unwrap() } } diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index 7cbc4dbde..dc877ae2d 100644 --- a/src/npyffi/objects.rs +++ b/src/npyffi/objects.rs @@ -59,7 +59,7 @@ pub struct PyArray_DescrProto { #[repr(C)] #[derive(Copy, Clone)] -pub struct _PyArray_DescrNumPy2 { +pub struct _PyArray_Descr_NumPy2 { pub ob_base: PyObject, pub typeobj: *mut PyTypeObject, pub kind: c_char, @@ -77,7 +77,7 @@ pub struct _PyArray_DescrNumPy2 { #[repr(C)] #[derive(Copy, Clone)] -struct _PyArray_LegacyDescr { +struct _PyArray_Descr_NumPy1 { pub ob_base: PyObject, pub typeobj: *mut PyTypeObject, pub kind: c_char, @@ -100,7 +100,7 @@ struct _PyArray_LegacyDescr { #[allow(non_snake_case)] #[inline(always)] pub unsafe fn PyDataType_ISLEGACY(dtype: *const PyArray_Descr) -> bool { - (*dtype).type_num < NPY_TYPES::NPY_VSTRING as i32 && (*dtype).type_num >= 0 + (*dtype).type_num < NPY_TYPES::NPY_VSTRING as _ && (*dtype).type_num >= 0 } #[allow(non_snake_case)] @@ -117,7 +117,7 @@ pub unsafe fn PyDataType_SET_ELSIZE<'py>( } } else { unsafe { - (*(dtype as *mut _PyArray_DescrNumPy2)).elsize = size; + (*(dtype as *mut _PyArray_Descr_NumPy2)).elsize = size; } } } @@ -129,23 +129,23 @@ pub unsafe fn PyDataType_FLAGS<'py>(py: Python<'py>, dtype: *const PyArray_Descr 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 } + unsafe { (*(dtype as *mut _PyArray_Descr_NumPy2)).flags } } } macro_rules! define_descr_accessor { - ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $zero:expr) => { + ($name:ident, $property:ident, $type:ty, $legacy_only:literal, $default:expr) => { #[allow(non_snake_case)] #[inline(always)] pub unsafe fn $name<'py>(py: Python<'py>, dtype: *const PyArray_Descr) -> $type { if $legacy_only && !PyDataType_ISLEGACY(dtype) { - $zero + $default } else { 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 } + unsafe { (*(dtype as *const _PyArray_Descr_NumPy1)).$property } } } } @@ -559,8 +559,8 @@ pub struct PyArray_DatetimeDTypeMetaData { pub meta: PyArray_DatetimeMetaData, } -// npy_packed_static_string and npy_string_allocator are opaque pointers -// consider extern types when they are stabilized +// npy_packed_static_string and npy_string_allocator are opaque pointers. +// FIXME(adamreichold): Consider extern types when they are stabilized. // https://github.com/rust-lang/rust/issues/43467 pub type npy_packed_static_string = c_void; pub type npy_string_allocator = c_void; From 61fb9e067ae5c02f69282f979882ae36a3e5ebf6 Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Sat, 31 Aug 2024 11:47:23 -0700 Subject: [PATCH 15/22] Simplify impl_api macro --- src/npyffi/array.rs | 96 ++++++++++++++++++++++----------------------- src/npyffi/mod.rs | 65 ++++++++++++------------------ 2 files changed, 74 insertions(+), 87 deletions(-) diff --git a/src/npyffi/array.rs b/src/npyffi/array.rs index 2e4192fc5..a4ce90082 100644 --- a/src/npyffi/array.rs +++ b/src/npyffi/array.rs @@ -68,7 +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]; - impl_api![50; ..=1.26; PyArray_CastTo(out: *mut PyArrayObject, mp: *mut PyArrayObject) -> c_int]; + impl_api![50; NumPy1; 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]; @@ -82,11 +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]; - 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![65; NumPy1; PyArray_ScalarFromObject(object: *mut PyObject) -> *mut PyObject]; + impl_api![65; NumPy2; PyArray_Pack(descr: *mut PyArray_Descr, item: *mut c_void, value: *const PyObject) -> *mut PyObject]; + impl_api![66; NumPy1; PyArray_GetCastFunc(descr: *mut PyArray_Descr, type_num: c_int) -> PyArray_VectorUnaryFunc]; + impl_api![67; NumPy1; PyArray_FromDims(nd: c_int, d: *mut c_int, type_: c_int) -> *mut PyObject]; + impl_api![68; NumPy1; PyArray_FromDimsAndDataAndDescr(nd: c_int, d: *mut c_int, descr: *mut PyArray_Descr, data: *mut c_char) -> *mut PyObject]; 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]; @@ -99,7 +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]; - impl_api![81; ..=1.26; PyArray_MoveInto(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int]; + impl_api![81; NumPy1; 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]; @@ -119,7 +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]; - impl_api![103; ..=1.26; PyArray_FillObjectArray(arr: *mut PyArrayObject, obj: *mut PyObject)]; + impl_api![103; NumPy1; 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]; @@ -131,14 +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]; - impl_api![115; ..=1.26; PyArray_NewFlagsObject(obj: *mut PyObject) -> *mut PyObject]; + impl_api![115; NumPy1; PyArray_NewFlagsObject(obj: *mut PyObject) -> *mut PyObject]; impl_api![116; PyArray_CanCastScalar(from: *mut PyTypeObject, to: *mut PyTypeObject) -> npy_bool]; - impl_api![117; ..=1.26; PyArray_CompareUCS4(s1: *mut npy_ucs4, s2: *mut npy_ucs4, len: usize) -> c_int]; + impl_api![117; NumPy1; 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)]; - impl_api![122; ..=1.26; PyArray_FieldNames(fields: *mut PyObject) -> *mut PyObject]; + impl_api![122; NumPy1; 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]; @@ -179,17 +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]; - 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![163; NumPy1; PyArray_As1D(op: *mut *mut PyObject, ptr: *mut *mut c_char, d1: *mut c_int, typecode: c_int) -> c_int]; + impl_api![164; NumPy1; PyArray_As2D(op: *mut *mut PyObject, ptr: *mut *mut *mut c_char, d1: *mut c_int, d2: *mut c_int, typecode: c_int) -> c_int]; impl_api![165; PyArray_Free(op: *mut PyObject, ptr: *mut c_void) -> c_int]; impl_api![166; PyArray_Converter(object: *mut PyObject, address: *mut *mut PyObject) -> c_int]; impl_api![167; PyArray_IntpFromSequence(seq: *mut PyObject, vals: *mut npy_intp, maxvals: c_int) -> c_int]; impl_api![168; PyArray_Concatenate(op: *mut PyObject, axis: c_int) -> *mut PyObject]; impl_api![169; PyArray_InnerProduct(op1: *mut PyObject, op2: *mut PyObject) -> *mut PyObject]; impl_api![170; PyArray_MatrixProduct(op1: *mut PyObject, op2: *mut PyObject) -> *mut PyObject]; - impl_api![171; ..=1.26; PyArray_CopyAndTranspose(op: *mut PyObject) -> *mut PyObject]; + impl_api![171; NumPy1; PyArray_CopyAndTranspose(op: *mut PyObject) -> *mut PyObject]; impl_api![172; PyArray_Correlate(op1: *mut PyObject, op2: *mut PyObject, mode: c_int) -> *mut PyObject]; - impl_api![173; ..=1.26; PyArray_TypestrConvert(itemsize: c_int, gentype: c_int) -> c_int]; + impl_api![173; NumPy1; 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]; @@ -213,28 +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]; - impl_api![197; ..=1.26; PyArray_ElementFromName(str: *mut c_char) -> c_int]; + impl_api![197; NumPy1; 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]; - impl_api![201; ..=1.26; _PyArray_SigintHandler(signum: c_int)]; - impl_api![202; ..=1.26; _PyArray_GetSigintBuf() -> *mut c_void]; + impl_api![201; NumPy1; _PyArray_SigintHandler(signum: c_int)]; + impl_api![202; NumPy1; _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]; - impl_api![208; ..=1.26; PyArray_CompareString(s1: *mut c_char, s2: *mut c_char, len: usize) -> c_int]; + impl_api![208; NumPy1; 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]; - 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![219; NumPy1; PyArray_SetDatetimeParseFunction(op: *mut PyObject)]; + impl_api![220; NumPy1; PyArray_DatetimeToDatetimeStruct(val: npy_datetime, fr: NPY_DATETIMEUNIT, result: *mut npy_datetimestruct)]; + impl_api![221; NumPy1; PyArray_TimedeltaToTimedeltaStruct(val: npy_timedelta, fr: NPY_DATETIMEUNIT, result: *mut npy_timedeltastruct)]; + impl_api![222; NumPy1; PyArray_DatetimeStructToDatetime(fr: NPY_DATETIMEUNIT, d: *mut npy_datetimestruct) -> npy_datetime]; + impl_api![223; NumPy1; PyArray_TimedeltaStructToTimedelta(fr: NPY_DATETIMEUNIT, d: *mut npy_timedeltastruct) -> npy_datetime]; 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]; @@ -289,7 +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]; - 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![278; NumPy1; PyArray_GetArrayParamsFromObject(op: *mut PyObject, requested_dtype: *mut PyArray_Descr, writeable: npy_bool, out_dtype: *mut *mut PyArray_Descr, out_ndim: *mut c_int, out_dims: *mut npy_intp, out_arr: *mut *mut PyArrayObject, context: *mut PyObject) -> c_int]; 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]; @@ -302,38 +302,38 @@ impl PyArrayAPI { impl_api![288; PyDataMem_NEW(size: usize) -> *mut c_void]; impl_api![289; PyDataMem_FREE(ptr: *mut c_void)]; impl_api![290; PyDataMem_RENEW(ptr: *mut c_void, size: usize) -> *mut c_void]; - impl_api![291; ..=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![291; NumPy1; PyDataMem_SetEventHook(newhook: PyDataMem_EventHookFunc, user_data: *mut c_void, old_data: *mut *mut c_void) -> PyDataMem_EventHookFunc]; + impl_api![293; NumPy1; PyArray_MapIterSwapAxes(mit: *mut PyArrayMapIterObject, ret: *mut *mut PyArrayObject, getmap: c_int)]; + impl_api![294; NumPy1; PyArray_MapIterArray(a: *mut PyArrayObject, index: *mut PyObject) -> *mut PyObject]; + impl_api![295; NumPy1; PyArray_MapIterNext(mit: *mut PyArrayMapIterObject)]; impl_api![296; PyArray_Partition(op: *mut PyArrayObject, ktharray: *mut PyArrayObject, axis: c_int, which: NPY_SELECTKIND) -> c_int]; impl_api![297; PyArray_ArgPartition(op: *mut PyArrayObject, ktharray: *mut PyArrayObject, axis: c_int, which: NPY_SELECTKIND) -> *mut PyObject]; impl_api![298; PyArray_SelectkindConverter(obj: *mut PyObject, selectkind: *mut NPY_SELECTKIND) -> c_int]; impl_api![299; PyDataMem_NEW_ZEROED(size: usize, elsize: usize) -> *mut c_void]; impl_api![300; PyArray_CheckAnyScalarExact(obj: *mut PyObject) -> c_int]; - impl_api![301; ..=1.26; PyArray_MapIterArrayCopyIfOverlap(a: *mut PyArrayObject, index: *mut PyObject, copy_if_overlap: c_int, extra_op: *mut PyArrayObject) -> *mut PyObject]; + impl_api![301; NumPy1; 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]; - 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]; + impl_api![307; NumPy2; NpyDatetime_ConvertDatetime64ToDatetimeStruct(meta: *mut PyArray_DatetimeMetaData, dt: npy_datetime, out: *mut npy_datetimestruct) -> c_int]; + impl_api![308; NumPy2; NpyDatetime_ConvertDatetimeStructToDatetime64(meta: *mut PyArray_DatetimeMetaData, dts: *const npy_datetimestruct, out: *mut npy_datetime) -> c_int]; + impl_api![309; NumPy2; NpyDatetime_ConvertPyDateTimeToDatetimeStruct(obj: *mut PyObject, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, apply_tzinfo: c_int) -> c_int]; + impl_api![310; NumPy2; NpyDatetime_GetDatetimeISO8601StrLen(local: c_int, base: NPY_DATETIMEUNIT) -> c_int]; + impl_api![311; NumPy2; NpyDatetime_MakeISO8601Datetime(dts: *mut npy_datetimestruct, outstr: *mut c_char, outlen: npy_intp, local: c_int, utc: c_int, base: NPY_DATETIMEUNIT, tzoffset: c_int, casting: NPY_CASTING) -> c_int]; + impl_api![312; NumPy2; NpyDatetime_ParseISO8601Datetime(str: *const c_char, len: pyo3::ffi::Py_ssize_t, unit: NPY_DATETIMEUNIT, casting: NPY_CASTING, out: *mut npy_datetimestruct, out_bestunit: *mut NPY_DATETIMEUNIT, out_special: *mut npy_bool) -> c_int]; + impl_api![313; NumPy2; NpyString_load(allocator: *mut npy_string_allocator, packed_string: *const npy_packed_static_string, unpacked_string: *mut npy_static_string) -> c_int]; + impl_api![314; NumPy2; NpyString_pack(out: *mut npy_packed_static_string) -> c_int]; + impl_api![315; NumPy2; NpyString_pack_null(allocator: *mut npy_string_allocator, packed_string: *mut npy_packed_static_string) -> c_int]; + impl_api![316; NumPy2; NpyString_acquire_allocator(descr: *const PyArray_StringDTypeObject) -> *mut npy_string_allocator]; + impl_api![317; NumPy2; NpyString_acquire_allocators(n_descriptors: usize, descrs: *const *mut PyArray_Descr, allocators: *mut *mut npy_string_allocator)]; + impl_api![318; NumPy2; NpyString_release_allocator(allocator: *mut npy_string_allocator)]; + impl_api![319; NumPy2; NpyString_release_allocators(length: usize, allocators: *mut *mut npy_string_allocator)]; + impl_api![361; NumPy2; PyArray_GetDefaultDescr(DType: *mut PyArray_DTypeMeta) -> *mut PyArray_Descr]; + impl_api![362; NumPy2; PyArrayInitDTypeMeta_FromSpec(DType: *mut PyArray_DTypeMeta, spec: *mut PyArrayDTypeMeta_Spec) -> c_int]; + impl_api![363; NumPy2; PyArray_CommonDType(dtype1: *mut PyArray_DTypeMeta, dtype2: *mut PyArray_DTypeMeta) -> PyArray_DTypeMeta]; + impl_api![364; NumPy2; PyArray_PromoteDTypeSequence(length: npy_intp, dtypes_in: *mut *mut PyArray_DTypeMeta) -> *mut PyArray_DTypeMeta]; + impl_api![365; NumPy2; _PyDataType_GetArrFuncs(descr: *const PyArray_Descr) -> *mut PyArray_ArrFuncs]; #[allow(non_snake_case)] pub unsafe fn PyArray_CopyInto<'py>( diff --git a/src/npyffi/mod.rs b/src/npyffi/mod.rs index 68ccf3619..f5295ea8e 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -45,22 +45,6 @@ fn get_numpy_api<'py>( Ok(api) } -const fn api_version_to_numpy_version_range(api_version: c_uint) -> (&'static str, &'static str) { - match api_version { - 0..=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..=c_uint::MAX => ("2.0", "?"), - } -} - // Implements wrappers for NumPy's Array and UFunc API macro_rules! impl_api { // API available on all versions @@ -73,36 +57,39 @@ macro_rules! impl_api { }; // 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)?] => { + [$offset: expr; NumPy1; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { #[allow(non_snake_case)] pub unsafe fn $fname<'py>(&self, py: Python<'py>, $($arg : $t), *) $(-> $ret)* { 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, - ) } )? + if api_version >= API_VERSION_2_0 { + panic!( + "{} requires API < {:08X} (NumPy 1) but the runtime version is API {:08X}", + stringify!($fname), + API_VERSION_2_0, + 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)*]; + [$offset: expr; NumPy2; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { + #[allow(non_snake_case)] + pub unsafe fn $fname<'py>(&self, py: Python<'py>, $($arg : $t), *) $(-> $ret)* { + let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); + if api_version < API_VERSION_2_0 { + panic!( + "{} requires API {:08X} or greater (NumPy 2) but the runtime version is API {:08X}", + stringify!($fname), + API_VERSION_2_0, + api_version, + ) + } + let fptr = self.get(py, $offset) as *const extern fn ($($arg: $t), *) $(-> $ret)*; + (*fptr)($($arg), *) + } + }; } From 17eda7f7dd97596fd273939d5e84372e881e410c Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Tue, 3 Sep 2024 06:55:58 -0700 Subject: [PATCH 16/22] lint --- src/dtype.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dtype.rs b/src/dtype.rs index c3e924d11..f6aeaac38 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -580,7 +580,7 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { let subarray = unsafe { PyDataType_SUBARRAY(self.py(), self.as_dtype_ptr()).as_ref() }; match subarray { None => 0, - Some(subarray) => unsafe { PyTuple_Size(subarray.shape) }.max(0) as _ + Some(subarray) => unsafe { PyTuple_Size(subarray.shape) }.max(0) as _, } } @@ -590,7 +590,7 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { None => self.clone(), Some(subarray) => unsafe { Bound::from_borrowed_ptr(self.py(), subarray.base.cast()).downcast_into_unchecked() - } + }, } } @@ -600,7 +600,8 @@ impl<'py> PyArrayDescrMethods<'py> for Bound<'py, PyArrayDescr> { None => Vec::new(), Some(subarray) => { // NumPy guarantees that shape is a tuple of non-negative integers so this should never panic. - unsafe { Borrowed::from_ptr(self.py(), subarray.shape) }.extract().unwrap() + let shape = unsafe { Borrowed::from_ptr(self.py(), subarray.shape) }; + shape.extract().unwrap() } } } From abebd0f331ad75cdb42c375a32bdb2f16f7b5b1c Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Fri, 20 Sep 2024 12:02:14 -0700 Subject: [PATCH 17/22] Fixes from review --- src/npyffi/array.rs | 14 ++----------- src/npyffi/mod.rs | 47 ++++++++++++++++++++----------------------- src/npyffi/objects.rs | 21 +++++++++---------- 3 files changed, 33 insertions(+), 49 deletions(-) diff --git a/src/npyffi/array.rs b/src/npyffi/array.rs index a4ce90082..7074f61c3 100644 --- a/src/npyffi/array.rs +++ b/src/npyffi/array.rs @@ -342,12 +342,7 @@ impl PyArrayAPI { dst: *mut PyArrayObject, src: *mut PyArrayObject, ) -> c_int { - let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); - let offset = if api_version < API_VERSION_2_0 { - 82 - } else { - 50 - }; + let offset = if is_numpy_2(py) { 50 } else { 82 }; let fptr = self.get(py, offset) as *const extern "C" fn(dst: *mut PyArrayObject, src: *mut PyArrayObject) -> c_int; (*fptr)(dst, src) @@ -360,12 +355,7 @@ impl PyArrayAPI { out: *mut PyArrayObject, mp: *mut PyArrayObject, ) -> c_int { - let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); - let offset = if api_version < API_VERSION_2_0 { - 83 - } else { - 51 - }; + let offset = if is_numpy_2(py) { 51 } else { 83 }; let fptr = self.get(py, offset) as *const extern "C" fn(out: *mut PyArrayObject, mp: *mut PyArrayObject) -> c_int; (*fptr)(out, mp) diff --git a/src/npyffi/mod.rs b/src/npyffi/mod.rs index f5295ea8e..8691c5bfd 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -20,7 +20,7 @@ use pyo3::{ pub const API_VERSION_2_0: c_uint = 0x00000012; -pub static API_VERSION: GILOnceCell = GILOnceCell::new(); +static API_VERSION: GILOnceCell = GILOnceCell::new(); fn get_numpy_api<'py>( py: Python<'py>, @@ -36,15 +36,16 @@ fn get_numpy_api<'py>( // so we can safely cache a pointer into its interior. forget(capsule); - 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) } +fn is_numpy_2<'py>(py: Python<'py>) -> bool { + let api_version = *API_VERSION.get_or_init(py, || unsafe { + PY_ARRAY_API.PyArray_GetNDArrayCFeatureVersion(py) + }); + api_version >= API_VERSION_2_0 +} + // Implements wrappers for NumPy's Array and UFunc API macro_rules! impl_api { // API available on all versions @@ -60,15 +61,13 @@ macro_rules! impl_api { [$offset: expr; NumPy1; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { #[allow(non_snake_case)] pub unsafe fn $fname<'py>(&self, py: Python<'py>, $($arg : $t), *) $(-> $ret)* { - let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); - if api_version >= API_VERSION_2_0 { - panic!( - "{} requires API < {:08X} (NumPy 1) but the runtime version is API {:08X}", - stringify!($fname), - API_VERSION_2_0, - api_version, - ) - } + assert!( + !is_numpy_2(py), + "{} requires API < {:08X} (NumPy 1) but the runtime version is API {:08X}", + stringify!($fname), + API_VERSION_2_0, + *API_VERSION.get(py).expect("API_VERSION is initialized"), + ); let fptr = self.get(py, $offset) as *const extern fn ($($arg: $t), *) $(-> $ret)*; (*fptr)($($arg), *) } @@ -77,15 +76,13 @@ macro_rules! impl_api { [$offset: expr; NumPy2; $fname: ident ($($arg: ident: $t: ty),* $(,)?) $(-> $ret: ty)?] => { #[allow(non_snake_case)] pub unsafe fn $fname<'py>(&self, py: Python<'py>, $($arg : $t), *) $(-> $ret)* { - let api_version = *API_VERSION.get(py).expect("API_VERSION is initialized"); - if api_version < API_VERSION_2_0 { - panic!( - "{} requires API {:08X} or greater (NumPy 2) but the runtime version is API {:08X}", - stringify!($fname), - API_VERSION_2_0, - api_version, - ) - } + assert!( + is_numpy_2(py), + "{} requires API {:08X} or greater (NumPy 2) but the runtime version is API {:08X}", + stringify!($fname), + API_VERSION_2_0, + *API_VERSION.get(py).expect("API_VERSION is initialized"), + ); let fptr = self.get(py, $offset) as *const extern fn ($($arg: $t), *) $(-> $ret)*; (*fptr)($($arg), *) } diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index dc877ae2d..8f3ee56ad 100644 --- a/src/npyffi/objects.rs +++ b/src/npyffi/objects.rs @@ -110,14 +110,13 @@ pub unsafe fn PyDataType_SET_ELSIZE<'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 { + if is_numpy_2(py) { unsafe { - (*(dtype as *mut PyArray_DescrProto)).elsize = size as c_int; + (*(dtype as *mut _PyArray_Descr_NumPy2)).elsize = size; } } else { unsafe { - (*(dtype as *mut _PyArray_Descr_NumPy2)).elsize = size; + (*(dtype as *mut PyArray_DescrProto)).elsize = size as c_int; } } } @@ -125,11 +124,10 @@ pub unsafe fn PyDataType_SET_ELSIZE<'py>( #[allow(non_snake_case)] #[inline(always)] 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 { + if is_numpy_2(py) { unsafe { (*(dtype as *mut _PyArray_Descr_NumPy2)).flags } + } else { + unsafe { (*(dtype as *mut PyArray_DescrProto)).flags as c_uchar as npy_uint64 } } } @@ -141,11 +139,10 @@ macro_rules! define_descr_accessor { if $legacy_only && !PyDataType_ISLEGACY(dtype) { $default } else { - 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 { + if is_numpy_2(py) { unsafe { (*(dtype as *const _PyArray_Descr_NumPy1)).$property } + } else { + unsafe { (*(dtype as *mut PyArray_DescrProto)).$property as $type } } } } From da0807f36df60f17ee65bd8c1521cb08f353f605 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 23 Sep 2024 21:41:26 +0100 Subject: [PATCH 18/22] Apply suggestions from code review --- src/npyffi/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/npyffi/mod.rs b/src/npyffi/mod.rs index 8691c5bfd..8a75dff8b 100644 --- a/src/npyffi/mod.rs +++ b/src/npyffi/mod.rs @@ -39,7 +39,8 @@ fn get_numpy_api<'py>( Ok(api) } -fn is_numpy_2<'py>(py: Python<'py>) -> bool { +/// Returns whether the runtime `numpy` version is 2.0 or greater. +pub fn is_numpy_2<'py>(py: Python<'py>) -> bool { let api_version = *API_VERSION.get_or_init(py, || unsafe { PY_ARRAY_API.PyArray_GetNDArrayCFeatureVersion(py) }); From 60133432a1ce36b33526dccefb52f5738b21676e Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Mon, 23 Sep 2024 14:46:23 -0700 Subject: [PATCH 19/22] Fix dtypes tests to work with numpy 1 or 2 --- src/dtype.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dtype.rs b/src/dtype.rs index f6aeaac38..bc68dbaa8 100644 --- a/src/dtype.rs +++ b/src/dtype.rs @@ -826,7 +826,7 @@ mod tests { use pyo3::{py_run, types::PyTypeMethods}; - use crate::npyffi::NPY_NEEDS_PYAPI; + use crate::npyffi::{is_numpy_2, NPY_NEEDS_PYAPI}; #[test] fn test_dtype_new() { @@ -855,7 +855,11 @@ mod tests { dtype_bound::(py).typeobj().qualname().unwrap() } Python::with_gil(|py| { - assert_eq!(type_name::(py), "bool_"); + if is_numpy_2(py) { + assert_eq!(type_name::(py), "bool"); + } else { + assert_eq!(type_name::(py), "bool_"); + } assert_eq!(type_name::(py), "int8"); assert_eq!(type_name::(py), "int16"); From 5dd5280c1f218eee4b5eeac5506ad0b252a4f86e Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Thu, 3 Oct 2024 10:43:32 -0700 Subject: [PATCH 20/22] Rename to _PyArray_DescrNumPy2 and _PyArray_LegacyDescr to match c code --- src/npyffi/objects.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/npyffi/objects.rs b/src/npyffi/objects.rs index 8f3ee56ad..fa0fb403c 100644 --- a/src/npyffi/objects.rs +++ b/src/npyffi/objects.rs @@ -59,7 +59,7 @@ pub struct PyArray_DescrProto { #[repr(C)] #[derive(Copy, Clone)] -pub struct _PyArray_Descr_NumPy2 { +pub struct _PyArray_DescrNumPy2 { pub ob_base: PyObject, pub typeobj: *mut PyTypeObject, pub kind: c_char, @@ -77,7 +77,7 @@ pub struct _PyArray_Descr_NumPy2 { #[repr(C)] #[derive(Copy, Clone)] -struct _PyArray_Descr_NumPy1 { +struct _PyArray_LegacyDescr { pub ob_base: PyObject, pub typeobj: *mut PyTypeObject, pub kind: c_char, @@ -112,7 +112,7 @@ pub unsafe fn PyDataType_SET_ELSIZE<'py>( ) { if is_numpy_2(py) { unsafe { - (*(dtype as *mut _PyArray_Descr_NumPy2)).elsize = size; + (*(dtype as *mut _PyArray_DescrNumPy2)).elsize = size; } } else { unsafe { @@ -125,7 +125,7 @@ pub unsafe fn PyDataType_SET_ELSIZE<'py>( #[inline(always)] pub unsafe fn PyDataType_FLAGS<'py>(py: Python<'py>, dtype: *const PyArray_Descr) -> npy_uint64 { if is_numpy_2(py) { - unsafe { (*(dtype as *mut _PyArray_Descr_NumPy2)).flags } + unsafe { (*(dtype as *mut _PyArray_DescrNumPy2)).flags } } else { unsafe { (*(dtype as *mut PyArray_DescrProto)).flags as c_uchar as npy_uint64 } } @@ -140,7 +140,7 @@ macro_rules! define_descr_accessor { $default } else { if is_numpy_2(py) { - unsafe { (*(dtype as *const _PyArray_Descr_NumPy1)).$property } + unsafe { (*(dtype as *const _PyArray_LegacyDescr)).$property } } else { unsafe { (*(dtype as *mut PyArray_DescrProto)).$property as $type } } From 43221025136af8a4cea0443a53b1939c2f1c913b Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Thu, 3 Oct 2024 10:48:17 -0700 Subject: [PATCH 21/22] Fail compilation on 32-bit windows --- .github/workflows/ci.yml | 13 ++++++++++--- src/lib.rs | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ccb66803..aa524f6ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,6 @@ jobs: { os: "ubuntu-latest", python-architecture: "x64", rust-target: "x86_64-unknown-linux-gnu" }, { os: "macOS-13", python-architecture: "x64", rust-target: "x86_64-apple-darwin" }, { os: "windows-latest", python-architecture: "x64", rust-target: "x86_64-pc-windows-msvc" }, - { os: "windows-latest", python-architecture: "x86", rust-target: "i686-pc-windows-msvc" }, ] include: # Older versions of CPython are not available for AArch64. @@ -84,21 +83,29 @@ jobs: RUST_BACKTRACE: 1 test-numpy1: - name: python${{ matrix.python-version }} numpy1 - runs-on: ubuntu-latest + name: python${{ matrix.python-version }}-${{ matrix.platform.python-architecture }} ${{ matrix.platform.os }} numpy1 + runs-on: ${{ matrix.platform.os }} needs: [lint, check-msrv, examples] strategy: fail-fast: ${{ !contains(github.event.pull_request.labels.*.name, 'CI-no-fail-fast') }} matrix: python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + platform: [ + { os: "ubuntu-latest", python-architecture: "x64", rust-target: "x86_64-unknown-linux-gnu" }, + { os: "macOS-13", python-architecture: "x64", rust-target: "x86_64-apple-darwin" }, + { os: "windows-latest", python-architecture: "x64", rust-target: "x86_64-pc-windows-msvc" }, + ] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + architecture: ${{ matrix.platform.python-architecture }} - name: Install Rust uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.platform.rust-target }} - name: Install toml run: pip install toml - name: Edit Cargo.toml and enable new resolver diff --git a/src/lib.rs b/src/lib.rs index cc218dd18..44dc09622 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,6 +72,9 @@ as well as the [`PyReadonlyArray::try_as_matrix`] and [`PyReadwriteArray::try_as #![deny(missing_docs, missing_debug_implementations)] +#[cfg(all(target_os = "windows", target_arch = "x86"))] +compile_error!("Compilation for 32-bit windows is not currently supported. See https://github.com/PyO3/rust-numpy/issues/448"); + pub mod array; mod array_like; pub mod borrow; From d09a35d86d31bf4683c611cf13ca62841c1734da Mon Sep 17 00:00:00 2001 From: Matthew Neeley Date: Thu, 3 Oct 2024 13:33:22 -0700 Subject: [PATCH 22/22] Add a changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ecd9080..05d9d2057 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Unreleased - Add `permute` and `transpose` methods for changing the order of axes of a `PyArray`. ([#428](https://github.com/PyO3/rust-numpy/pull/428)) + - Add support for NumPy v2 which had a number of changes to the [C API](https://numpy.org/devdocs/numpy_2_0_migration_guide.html#c-api-changes). ([#442](https://github.com/PyO3/rust-numpy/pull/442)) - v0.21.0 - Migrate to the new `Bound` API introduced by PyO3 0.21. ([#410](https://github.com/PyO3/rust-numpy/pull/410)) ([#411](https://github.com/PyO3/rust-numpy/pull/411)) ([#412](https://github.com/PyO3/rust-numpy/pull/412)) ([#415](https://github.com/PyO3/rust-numpy/pull/415)) ([#416](https://github.com/PyO3/rust-numpy/pull/416)) ([#418](https://github.com/PyO3/rust-numpy/pull/418)) ([#419](https://github.com/PyO3/rust-numpy/pull/419)) ([#420](https://github.com/PyO3/rust-numpy/pull/420)) ([#421](https://github.com/PyO3/rust-numpy/pull/421)) ([#422](https://github.com/PyO3/rust-numpy/pull/422))