Skip to content

Commit 84ebcb6

Browse files
committed
[numpy] numpy 1.x and 2.x compatibility header
1 parent 5a60764 commit 84ebcb6

File tree

3 files changed

+64
-32
lines changed

3 files changed

+64
-32
lines changed

src/decoder.c

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,7 @@
2525
#include "markers.h"
2626
#include "decoder.h"
2727
#include "python_funcs.h"
28-
29-
/******************************************************************************/
30-
/* NumPy 1.x/2.x compatibility macros */
31-
/******************************************************************************/
32-
33-
/* Check for NumPy 2.x using NPY_ABI_VERSION if available, otherwise assume 1.x */
34-
#if defined(NPY_ABI_VERSION) && NPY_ABI_VERSION >= 0x02000000
35-
/* NumPy 2.x: macros are provided by NumPy headers */
36-
#else
37-
/* NumPy 1.x: define compatibility macros */
38-
#define PyDataType_ELSIZE(d) ((d)->elsize)
39-
#define PyDataType_TYPE_NUM(d) ((d)->type_num)
40-
#endif
28+
#include "numpy_compat.h"
4129

4230
/******************************************************************************/
4331

src/encoder.c

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,7 @@
2626
#include "markers.h"
2727
#include "encoder.h"
2828
#include "python_funcs.h"
29-
30-
/******************************************************************************/
31-
/* NumPy 1.x/2.x compatibility macros */
32-
/******************************************************************************/
33-
34-
/* Check for NumPy 2.x using NPY_ABI_VERSION if available, otherwise assume 1.x */
35-
#if defined(NPY_ABI_VERSION) && NPY_ABI_VERSION >= 0x02000000
36-
/* NumPy 2.x: macros are provided by NumPy headers */
37-
#else
38-
/* NumPy 1.x: define compatibility macros */
39-
#define PyDataType_ELSIZE(d) ((d)->elsize)
40-
#define PyDataType_TYPE_NUM(d) ((d)->type_num)
41-
#endif
29+
#include "numpy_compat.h"
4230

4331
/******************************************************************************/
4432

@@ -339,7 +327,7 @@ static inline int _is_string_type(int type_num) {
339327

340328
/* Recursively check if a dtype is supported for SOA encoding */
341329
static int _is_soa_compatible_dtype(PyArray_Descr* fd) {
342-
int type_num = PyDataType_TYPE_NUM(fd);
330+
int type_num = DESCR_TYPE_NUM(fd);
343331

344332
/* String types are supported */
345333
if (_is_string_type(type_num)) {
@@ -481,7 +469,7 @@ static int _encode_field_name(PyObject* name, _bjdata_encoder_buffer_t* buffer)
481469

482470
/* Write schema for a field recursively (handles nested structs) */
483471
static int _write_field_schema_recursive(PyArray_Descr* fd, _bjdata_encoder_buffer_t* buffer) {
484-
int type_num = PyDataType_TYPE_NUM(fd);
472+
int type_num = DESCR_TYPE_NUM(fd);
485473
Py_ssize_t i;
486474

487475
/* Handle NPY_VOID: could be sub-array or nested struct */
@@ -501,7 +489,7 @@ static int _write_field_schema_recursive(PyArray_Descr* fd, _bjdata_encoder_buff
501489
}
502490
}
503491

504-
int base_type = PyDataType_TYPE_NUM(base_dtype);
492+
int base_type = DESCR_TYPE_NUM(base_dtype);
505493
Py_DECREF(subdtype);
506494

507495
/* Write sub-array schema: [TTT...] */
@@ -589,7 +577,7 @@ static int _write_field_schema_recursive(PyArray_Descr* fd, _bjdata_encoder_buff
589577
/* String types - write NUMPY BYTE SIZE (not character count) */
590578
if (_is_string_type(type_num)) {
591579
WRITE_CHAR_OR_BAIL(TYPE_STRING);
592-
BAIL_ON_NONZERO(_encode_longlong(PyDataType_ELSIZE(fd), buffer));
580+
BAIL_ON_NONZERO(_encode_longlong(DESCR_ELSIZE(fd), buffer));
593581
return 0;
594582
}
595583

@@ -929,9 +917,9 @@ static int _encode_soa(PyArrayObject* arr, _bjdata_encoder_buffer_t* buffer, int
929917

930918
PyArray_Descr* fd = (PyArray_Descr*)PyTuple_GET_ITEM(info, 0);
931919
field_offset[i] = PyLong_AsSsize_t(PyTuple_GET_ITEM(info, 1));
932-
field_itemsize[i] = PyDataType_ELSIZE(fd);
920+
field_itemsize[i] = DESCR_ELSIZE(fd);
933921

934-
int type_num = PyDataType_TYPE_NUM(fd);
922+
int type_num = DESCR_TYPE_NUM(fd);
935923

936924
if (type_num == NPY_BOOL) {
937925
field_type[i] = 1;

src/numpy_compat.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* NumPy 1.x/2.x compatibility macros for PyArray_Descr access
3+
*
4+
* This version avoids using NumPy 2.x API functions by using
5+
* PyObject attribute access, which works in both versions.
6+
*
7+
* Include this AFTER Python.h and numpy headers.
8+
*/
9+
10+
#ifndef NPY_COMPAT_H
11+
#define NPY_COMPAT_H
12+
13+
#include <numpy/ndarraytypes.h>
14+
15+
#if NPY_ABI_VERSION < 0x02000000
16+
/*
17+
* NumPy 1.x: Direct struct member access
18+
*/
19+
#define DESCR_ELSIZE(d) (((PyArray_Descr*)(d))->elsize)
20+
#define DESCR_TYPE_NUM(d) (((PyArray_Descr*)(d))->type_num)
21+
#else
22+
/*
23+
* NumPy 2.x: Use Python attribute access to avoid API linkage issues
24+
* This is slightly slower but guaranteed to work.
25+
*/
26+
static inline npy_intp _descr_elsize_compat(PyArray_Descr* d) {
27+
npy_intp result = 0;
28+
PyObject* val = PyObject_GetAttrString((PyObject*)d, "itemsize");
29+
30+
if (val) {
31+
result = PyLong_AsSsize_t(val);
32+
Py_DECREF(val);
33+
}
34+
35+
PyErr_Clear();
36+
return result;
37+
}
38+
39+
static inline int _descr_type_num_compat(PyArray_Descr* d) {
40+
int result = 0;
41+
PyObject* val = PyObject_GetAttrString((PyObject*)d, "num");
42+
43+
if (val) {
44+
result = (int)PyLong_AsLong(val);
45+
Py_DECREF(val);
46+
}
47+
48+
PyErr_Clear();
49+
return result;
50+
}
51+
52+
#define DESCR_ELSIZE(d) _descr_elsize_compat((PyArray_Descr*)(d))
53+
#define DESCR_TYPE_NUM(d) _descr_type_num_compat((PyArray_Descr*)(d))
54+
#endif
55+
56+
#endif /* NPY_COMPAT_H */

0 commit comments

Comments
 (0)