Skip to content

Commit dbbf235

Browse files
authored
Merge pull request numpy#25943 from seberg/descr-abi-break
API: Restructure the dtype struct to be new dtype friendly
2 parents a174433 + 2d84ae6 commit dbbf235

File tree

15 files changed

+207
-157
lines changed

15 files changed

+207
-157
lines changed

doc/source/reference/c-api/types-and-structures.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ PyArrayDescr_Type and PyArray_Descr
290290
npy_intp alignment;
291291
NpyAuxData *c_metadata;
292292
npy_hash_t hash;
293-
void *reserved_null; // unused field, must be NULL.
293+
void *reserved_null[2]; // unused field, must be NULLed.
294294
} PyArray_Descr;
295295
296296
Some dtypes have additional members which are accessible through

numpy/__init__.cython-30.pxd

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,31 @@ cdef extern from "numpy/arrayobject.h":
289289
# directly accessing this field.
290290
cdef char byteorder
291291
cdef int type_num
292-
cdef int itemsize "elsize"
293-
cdef int alignment
294-
cdef object fields
295-
cdef tuple names
292+
293+
@property
294+
cdef inline npy_intp itemsize(self) nogil:
295+
return PyDataType_ELSIZE(self)
296+
297+
@property
298+
cdef inline npy_intp alignment(self) nogil:
299+
return PyDataType_ALIGNMENT(self)
300+
301+
# Use fields/names with care as they may be NULL. You must check
302+
# for this using PyDataType_HASFIELDS.
303+
@property
304+
cdef inline object fields(self):
305+
return <object>PyDataType_FIELDS(self)
306+
307+
@property
308+
cdef inline tuple names(self):
309+
return <tuple>PyDataType_NAMES(self)
310+
296311
# Use PyDataType_HASSUBARRAY to test whether this field is
297312
# valid (the pointer can be NULL). Most users should access
298313
# this field via the inline helper method PyDataType_SHAPE.
299-
cdef PyArray_ArrayDescr* subarray
314+
@property
315+
cdef inline PyArray_ArrayDescr* subarray(self) nogil:
316+
return PyDataType_SUBARRAY(self)
300317

301318
@property
302319
cdef inline npy_uint64 flags(self) nogil:
@@ -455,6 +472,13 @@ cdef extern from "numpy/arrayobject.h":
455472
bint PyTypeNum_ISEXTENDED(int) nogil
456473
bint PyTypeNum_ISOBJECT(int) nogil
457474

475+
npy_intp PyDataType_ELSIZE(dtype) nogil
476+
npy_intp PyDataType_ALIGNMENT(dtype) nogil
477+
PyObject* PyDataType_METADATA(dtype) nogil
478+
PyArray_ArrayDescr* PyDataType_SUBARRAY(dtype) nogil
479+
PyObject* PyDataType_NAMES(dtype) nogil
480+
PyObject* PyDataType_FIELDS(dtype) nogil
481+
458482
bint PyDataType_ISBOOL(dtype) nogil
459483
bint PyDataType_ISUNSIGNED(dtype) nogil
460484
bint PyDataType_ISSIGNED(dtype) nogil

numpy/__init__.pxd

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -286,14 +286,9 @@ cdef extern from "numpy/arrayobject.h":
286286
# Flags are not directly accessible on Cython <3. Use PyDataType_FLAGS.
287287
# cdef char flags
288288
cdef int type_num
289-
cdef int itemsize "elsize"
290-
cdef int alignment
291-
cdef object fields
292-
cdef tuple names
293-
# Use PyDataType_HASSUBARRAY to test whether this field is
294-
# valid (the pointer can be NULL). Most users should access
295-
# this field via the inline helper method PyDataType_SHAPE.
296-
cdef PyArray_ArrayDescr* subarray
289+
# itemsize/elsize, alignment, fields, names, and subarray must
290+
# use the `PyDataType_*` accessor macros. With Cython 3 you can
291+
# still use getter attributes `dtype.itemsize`
297292

298293
ctypedef class numpy.flatiter [object PyArrayIterObject, check_size ignore]:
299294
# Use through macros
@@ -375,6 +370,14 @@ cdef extern from "numpy/arrayobject.h":
375370
bint PyTypeNum_ISEXTENDED(int) nogil
376371
bint PyTypeNum_ISOBJECT(int) nogil
377372

373+
npy_intp PyDataType_ELSIZE(dtype) nogil
374+
void PyDataType_SET_ELSIZE(dtype, npy_intp) nogil
375+
npy_intp PyDataType_ALIGNMENT(dtype) nogil
376+
PyObject* PyDataType_METADATA(dtype) nogil
377+
PyArray_ArrayDescr* PyDataType_SUBARRAY(dtype) nogil
378+
PyObject* PyDataType_NAMES(dtype) nogil
379+
PyObject* PyDataType_FIELDS(dtype) nogil
380+
378381
bint PyDataType_ISBOOL(dtype) nogil
379382
bint PyDataType_ISUNSIGNED(dtype) nogil
380383
bint PyDataType_ISSIGNED(dtype) nogil

numpy/_core/include/numpy/ndarrayobject.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,17 @@ NPY_TITLE_KEY_check(PyObject *key, PyObject *value)
245245
* part of `ndarraytypes.h` which tries to be self contained.
246246
*/
247247

248+
static inline npy_intp
249+
PyArray_ITEMSIZE(const PyArrayObject *arr)
250+
{
251+
return PyDataType_ELSIZE(((PyArrayObject_fields *)arr)->descr);
252+
}
253+
254+
#define PyDataType_HASFIELDS(obj) (PyDataType_ISLEGACY((PyArray_Descr*)(obj)) && PyDataType_NAMES((PyArray_Descr*)(obj)) != NULL)
255+
#define PyDataType_HASSUBARRAY(dtype) (PyDataType_ISLEGACY(dtype) && PyDataType_SUBARRAY(dtype) != NULL)
256+
#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0 && \
257+
!PyDataType_HASFIELDS(dtype))
258+
248259
#define PyDataType_FLAGCHK(dtype, flag) \
249260
((PyDataType_FLAGS(dtype) & (flag)) == (flag))
250261

numpy/_core/include/numpy/ndarraytypes.h

Lines changed: 43 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -573,9 +573,9 @@ typedef struct {
573573
NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT | \
574574
NPY_NEEDS_INIT | NPY_NEEDS_PYAPI)
575575

576-
#if !(defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD)
576+
#if NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION
577577
/*
578-
* Public version of the Descriptor struct
578+
* Public version of the Descriptor struct as of 2.x
579579
*/
580580
typedef struct _PyArray_Descr {
581581
PyObject_HEAD
@@ -595,96 +595,78 @@ typedef struct _PyArray_Descr {
595595
* (not-applicable), or '=' (native).
596596
*/
597597
char byteorder;
598-
/* flags describing data type */
599-
char flags;
598+
/* Former flags flags space (unused) to ensure type_num is stable. */
599+
char _former_flags;
600600
/* number representing this type */
601601
int type_num;
602+
/* Space for dtype instance specific flags. */
603+
npy_uint64 flags;
602604
/* element size (itemsize) for this type */
603-
int elsize;
605+
npy_intp elsize;
604606
/* alignment needed for this type */
605-
int alignment;
606-
/*
607-
* Non-NULL if this type is
608-
* is an array (C-contiguous)
609-
* of some other type
610-
*/
611-
struct _arr_descr *subarray;
612-
/*
613-
* The fields dictionary for this type
614-
* For statically defined descr this
615-
* is always Py_None
616-
*/
617-
PyObject *fields;
618-
/*
619-
* An ordered tuple of field names or NULL
620-
* if no fields are defined
621-
*/
622-
PyObject *names;
623-
// TODO: Remove: still there to break all downstream nightlies once only
624-
void *_former_f;
625-
/* Metadata about this dtype */
607+
npy_intp alignment;
608+
/* metadata dict or NULL */
626609
PyObject *metadata;
627-
/*
628-
* Metadata specific to the C implementation
629-
* of the particular dtype. This was added
630-
* for NumPy 1.7.0.
631-
*/
632-
NpyAuxData *c_metadata;
633-
/* Cached hash value (-1 if not yet computed).
634-
* This was added for NumPy 2.0.0.
635-
*/
610+
/* Cached hash value (-1 if not yet computed). */
636611
npy_hash_t hash;
637-
612+
/* Unused slot (must be initialized to NULL) for future use */
613+
void *reserved_null[2];
638614
} PyArray_Descr;
639615

640-
#else /* internal build */
616+
#else /* 1.x and 2.x compatible version (only shared fields): */
641617

642-
// TODO: This split definition only exists for piece-meal transitioning
643-
// as it allows change internal use without worrying about public API.
644618
typedef struct _PyArray_Descr {
645619
PyObject_HEAD
646620
PyTypeObject *typeobj;
647621
char kind;
648622
char type;
649623
char byteorder;
650-
char flags;
624+
char _former_flags;
651625
int type_num;
652-
int elsize;
653-
int alignment;
654-
/* except hash, the below fields will be legacy descriptor specific */
655-
struct _arr_descr *unreachable_subarray;
656-
PyObject *unreachable_fields;
657-
PyObject *unreachable_names;
658-
PyArray_ArrFuncs *_former_f;
659-
PyObject *metadata;
660-
NpyAuxData *unreachable_c_metadata;
661-
npy_hash_t hash;
662626
} PyArray_Descr;
663627

664-
#endif /* internal build */
628+
/* To access modified fields, define the full 2.0 struct: */
629+
typedef struct {
630+
PyObject_HEAD
631+
PyTypeObject *typeobj;
632+
char kind;
633+
char type;
634+
char byteorder;
635+
char _former_flags;
636+
int type_num;
637+
npy_uint64 flags;
638+
npy_intp elsize;
639+
npy_intp alignment;
640+
PyObject *metadata;
641+
npy_hash_t hash;
642+
void *reserved_null[2];
643+
} _PyArray_DescrNumPy2;
665644

645+
#endif /* 1.x and 2.x compatible version */
666646

667647
/*
668648
* Semi-private struct with additional field of legacy descriptors (must
669-
* check NPY_DT_is_legacy before casting/accessing).
649+
* check NPY_DT_is_legacy before casting/accessing). The struct is also not
650+
* valid when running on 1.x (i.e. in public API use).
670651
*/
671652
typedef struct {
672653
PyObject_HEAD
673654
PyTypeObject *typeobj;
674655
char kind;
675656
char type;
676657
char byteorder;
677-
char flags;
658+
char _former_flags;
678659
int type_num;
679-
int elsize;
680-
int alignment;
660+
npy_uint64 flags;
661+
npy_intp elsize;
662+
npy_intp alignment;
663+
PyObject *metadata;
664+
npy_hash_t hash;
665+
void *reserved_null[2];
681666
struct _arr_descr *subarray;
682667
PyObject *fields;
683668
PyObject *names;
684-
PyArray_ArrFuncs *_former_f;
685-
PyObject *metadata;
686669
NpyAuxData *c_metadata;
687-
npy_hash_t hash;
688670
} _PyArray_LegacyDescr;
689671

690672

@@ -1569,11 +1551,6 @@ PyArray_FLAGS(const PyArrayObject *arr)
15691551
return ((PyArrayObject_fields *)arr)->flags;
15701552
}
15711553

1572-
static inline npy_intp
1573-
PyArray_ITEMSIZE(const PyArrayObject *arr)
1574-
{
1575-
return ((PyArrayObject_fields *)arr)->descr->elsize;
1576-
}
15771554

15781555
static inline int
15791556
PyArray_TYPE(const PyArrayObject *arr)
@@ -1687,42 +1664,13 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags)
16871664
#define PyDataType_ISUSERDEF(obj) PyTypeNum_ISUSERDEF(((PyArray_Descr*)(obj))->type_num)
16881665
#define PyDataType_ISEXTENDED(obj) PyTypeNum_ISEXTENDED(((PyArray_Descr*)(obj))->type_num)
16891666
#define PyDataType_ISOBJECT(obj) PyTypeNum_ISOBJECT(((PyArray_Descr*)(obj))->type_num)
1690-
#define PyDataType_HASFIELDS(obj) (PyDataType_ISLEGACY((PyArray_Descr*)(obj)) && ((_PyArray_LegacyDescr *)(obj))->names != NULL)
1691-
#define PyDataType_HASSUBARRAY(dtype) (PyDataType_ISLEGACY(dtype) && ((_PyArray_LegacyDescr *)dtype)->subarray != NULL)
1692-
#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0 && \
1693-
!PyDataType_HASFIELDS(dtype))
16941667
#define PyDataType_MAKEUNSIZED(dtype) ((dtype)->elsize = 0)
16951668
/*
1696-
* PyDataType_FLAGS, PyDataType_FLACHK, and PyDataType_REFCHK require
1697-
* npy_2_compat.h and are not defined here.
1669+
* PyDataType_* FLAGS, FLACHK, REFCHK, HASFIELDS, HASSUBARRAY, UNSIZED,
1670+
* SUBARRAY, NAMES, FIELDS, C_METADATA, and METADATA require version specific
1671+
* lookup and are defined in npy_2_compat.h.
16981672
*/
16991673

1700-
/*
1701-
* Access inline functions for legacy fields. Except metadata these fields are
1702-
* specific to structured arrays (names, fields) or datetime (c_metadata).
1703-
* Although technically they may be used (but normally ignored) on non-struct
1704-
* dtypes as well.
1705-
* For structured dtypes, new ways to define and access fields make sense.
1706-
*/
1707-
static inline PyArray_ArrayDescr *
1708-
PyDataType_SUBARRAY(PyArray_Descr *dtype) {
1709-
return !PyDataType_ISLEGACY(dtype) ? NULL : ((_PyArray_LegacyDescr *)dtype)->subarray;
1710-
}
1711-
1712-
static inline PyObject *
1713-
PyDataType_NAMES(PyArray_Descr *dtype) {
1714-
return !PyDataType_ISLEGACY(dtype) ? NULL : ((_PyArray_LegacyDescr *)dtype)->names;
1715-
}
1716-
1717-
static inline PyObject *
1718-
PyDataType_FIELDS(PyArray_Descr *dtype) {
1719-
return !PyDataType_ISLEGACY(dtype) ? NULL : ((_PyArray_LegacyDescr *)dtype)->fields;
1720-
}
1721-
1722-
static inline NpyAuxData *
1723-
PyDataType_C_METADATA(PyArray_Descr *dtype) {
1724-
return !PyDataType_ISLEGACY(dtype) ? NULL : ((_PyArray_LegacyDescr *)dtype)->c_metadata;
1725-
}
17261674

17271675
#define PyArray_ISBOOL(obj) PyTypeNum_ISBOOL(PyArray_TYPE(obj))
17281676
#define PyArray_ISUNSIGNED(obj) PyTypeNum_ISUNSIGNED(PyArray_TYPE(obj))
@@ -1967,13 +1915,4 @@ typedef struct {
19671915
*/
19681916
#undef NPY_DEPRECATED_INCLUDES
19691917

1970-
#if defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD
1971-
/*
1972-
* we use ndarraytypes.h alone sometimes, but some functions from
1973-
* npy_2_compat.h are forward declared here, so ensure we have them.
1974-
* (external libraries must eventually include `ndarrayobject.h`)
1975-
*/
1976-
#include "npy_2_compat.h"
1977-
#endif
1978-
19791918
#endif /* NUMPY_CORE_INCLUDE_NUMPY_NDARRAYTYPES_H_ */

0 commit comments

Comments
 (0)