diff --git a/doc/source/reference/c-api/types-and-structures.rst b/doc/source/reference/c-api/types-and-structures.rst index 811fb7ee9504..df32b3dfcd60 100644 --- a/doc/source/reference/c-api/types-and-structures.rst +++ b/doc/source/reference/c-api/types-and-structures.rst @@ -290,7 +290,7 @@ PyArrayDescr_Type and PyArray_Descr npy_intp alignment; NpyAuxData *c_metadata; npy_hash_t hash; - void *reserved_null; // unused field, must be NULL. + void *reserved_null[2]; // unused field, must be NULLed. } PyArray_Descr; Some dtypes have additional members which are accessible through diff --git a/numpy/__init__.cython-30.pxd b/numpy/__init__.cython-30.pxd index 369f8aa7b88c..1afbe3d8ebd0 100644 --- a/numpy/__init__.cython-30.pxd +++ b/numpy/__init__.cython-30.pxd @@ -289,14 +289,31 @@ cdef extern from "numpy/arrayobject.h": # directly accessing this field. cdef char byteorder cdef int type_num - cdef int itemsize "elsize" - cdef int alignment - cdef object fields - cdef tuple names + + @property + cdef inline npy_intp itemsize(self) nogil: + return PyDataType_ELSIZE(self) + + @property + cdef inline npy_intp alignment(self) nogil: + return PyDataType_ALIGNMENT(self) + + # Use fields/names with care as they may be NULL. You must check + # for this using PyDataType_HASFIELDS. + @property + cdef inline object fields(self): + return PyDataType_FIELDS(self) + + @property + cdef inline tuple names(self): + return PyDataType_NAMES(self) + # Use PyDataType_HASSUBARRAY to test whether this field is # valid (the pointer can be NULL). Most users should access # this field via the inline helper method PyDataType_SHAPE. - cdef PyArray_ArrayDescr* subarray + @property + cdef inline PyArray_ArrayDescr* subarray(self) nogil: + return PyDataType_SUBARRAY(self) @property cdef inline npy_uint64 flags(self) nogil: @@ -455,6 +472,13 @@ cdef extern from "numpy/arrayobject.h": bint PyTypeNum_ISEXTENDED(int) nogil bint PyTypeNum_ISOBJECT(int) nogil + npy_intp PyDataType_ELSIZE(dtype) nogil + npy_intp PyDataType_ALIGNMENT(dtype) nogil + PyObject* PyDataType_METADATA(dtype) nogil + PyArray_ArrayDescr* PyDataType_SUBARRAY(dtype) nogil + PyObject* PyDataType_NAMES(dtype) nogil + PyObject* PyDataType_FIELDS(dtype) nogil + bint PyDataType_ISBOOL(dtype) nogil bint PyDataType_ISUNSIGNED(dtype) nogil bint PyDataType_ISSIGNED(dtype) nogil diff --git a/numpy/__init__.pxd b/numpy/__init__.pxd index d801bb445819..096714f6d7cd 100644 --- a/numpy/__init__.pxd +++ b/numpy/__init__.pxd @@ -286,14 +286,9 @@ cdef extern from "numpy/arrayobject.h": # Flags are not directly accessible on Cython <3. Use PyDataType_FLAGS. # cdef char flags cdef int type_num - cdef int itemsize "elsize" - cdef int alignment - cdef object fields - cdef tuple names - # Use PyDataType_HASSUBARRAY to test whether this field is - # valid (the pointer can be NULL). Most users should access - # this field via the inline helper method PyDataType_SHAPE. - cdef PyArray_ArrayDescr* subarray + # itemsize/elsize, alignment, fields, names, and subarray must + # use the `PyDataType_*` accessor macros. With Cython 3 you can + # still use getter attributes `dtype.itemsize` ctypedef class numpy.flatiter [object PyArrayIterObject, check_size ignore]: # Use through macros @@ -375,6 +370,14 @@ cdef extern from "numpy/arrayobject.h": bint PyTypeNum_ISEXTENDED(int) nogil bint PyTypeNum_ISOBJECT(int) nogil + npy_intp PyDataType_ELSIZE(dtype) nogil + void PyDataType_SET_ELSIZE(dtype, npy_intp) nogil + npy_intp PyDataType_ALIGNMENT(dtype) nogil + PyObject* PyDataType_METADATA(dtype) nogil + PyArray_ArrayDescr* PyDataType_SUBARRAY(dtype) nogil + PyObject* PyDataType_NAMES(dtype) nogil + PyObject* PyDataType_FIELDS(dtype) nogil + bint PyDataType_ISBOOL(dtype) nogil bint PyDataType_ISUNSIGNED(dtype) nogil bint PyDataType_ISSIGNED(dtype) nogil diff --git a/numpy/_core/include/numpy/ndarrayobject.h b/numpy/_core/include/numpy/ndarrayobject.h index 9233ef32f8ef..f06bafe5b52a 100644 --- a/numpy/_core/include/numpy/ndarrayobject.h +++ b/numpy/_core/include/numpy/ndarrayobject.h @@ -245,6 +245,17 @@ NPY_TITLE_KEY_check(PyObject *key, PyObject *value) * part of `ndarraytypes.h` which tries to be self contained. */ +static inline npy_intp +PyArray_ITEMSIZE(const PyArrayObject *arr) +{ + return PyDataType_ELSIZE(((PyArrayObject_fields *)arr)->descr); +} + +#define PyDataType_HASFIELDS(obj) (PyDataType_ISLEGACY((PyArray_Descr*)(obj)) && PyDataType_NAMES((PyArray_Descr*)(obj)) != NULL) +#define PyDataType_HASSUBARRAY(dtype) (PyDataType_ISLEGACY(dtype) && PyDataType_SUBARRAY(dtype) != NULL) +#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0 && \ + !PyDataType_HASFIELDS(dtype)) + #define PyDataType_FLAGCHK(dtype, flag) \ ((PyDataType_FLAGS(dtype) & (flag)) == (flag)) diff --git a/numpy/_core/include/numpy/ndarraytypes.h b/numpy/_core/include/numpy/ndarraytypes.h index f86447369ab2..31aa3e4d330e 100644 --- a/numpy/_core/include/numpy/ndarraytypes.h +++ b/numpy/_core/include/numpy/ndarraytypes.h @@ -573,9 +573,9 @@ typedef struct { NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT | \ NPY_NEEDS_INIT | NPY_NEEDS_PYAPI) -#if !(defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD) +#if NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION /* - * Public version of the Descriptor struct + * Public version of the Descriptor struct as of 2.x */ typedef struct _PyArray_Descr { PyObject_HEAD @@ -595,78 +595,59 @@ typedef struct _PyArray_Descr { * (not-applicable), or '=' (native). */ char byteorder; - /* flags describing data type */ - char flags; + /* Former flags flags space (unused) to ensure type_num is stable. */ + char _former_flags; /* number representing this type */ int type_num; + /* Space for dtype instance specific flags. */ + npy_uint64 flags; /* element size (itemsize) for this type */ - int elsize; + npy_intp elsize; /* alignment needed for this type */ - int alignment; - /* - * Non-NULL if this type is - * is an array (C-contiguous) - * of some other type - */ - struct _arr_descr *subarray; - /* - * The fields dictionary for this type - * For statically defined descr this - * is always Py_None - */ - PyObject *fields; - /* - * An ordered tuple of field names or NULL - * if no fields are defined - */ - PyObject *names; - // TODO: Remove: still there to break all downstream nightlies once only - void *_former_f; - /* Metadata about this dtype */ + npy_intp alignment; + /* metadata dict or NULL */ PyObject *metadata; - /* - * Metadata specific to the C implementation - * of the particular dtype. This was added - * for NumPy 1.7.0. - */ - NpyAuxData *c_metadata; - /* Cached hash value (-1 if not yet computed). - * This was added for NumPy 2.0.0. - */ + /* Cached hash value (-1 if not yet computed). */ npy_hash_t hash; - + /* Unused slot (must be initialized to NULL) for future use */ + void *reserved_null[2]; } PyArray_Descr; -#else /* internal build */ +#else /* 1.x and 2.x compatible version (only shared fields): */ -// TODO: This split definition only exists for piece-meal transitioning -// as it allows change internal use without worrying about public API. typedef struct _PyArray_Descr { PyObject_HEAD PyTypeObject *typeobj; char kind; char type; char byteorder; - char flags; + char _former_flags; int type_num; - int elsize; - int alignment; - /* except hash, the below fields will be legacy descriptor specific */ - struct _arr_descr *unreachable_subarray; - PyObject *unreachable_fields; - PyObject *unreachable_names; - PyArray_ArrFuncs *_former_f; - PyObject *metadata; - NpyAuxData *unreachable_c_metadata; - npy_hash_t hash; } PyArray_Descr; -#endif /* internal build */ +/* To access modified fields, define the full 2.0 struct: */ +typedef struct { + PyObject_HEAD + PyTypeObject *typeobj; + char kind; + char type; + char byteorder; + char _former_flags; + int type_num; + npy_uint64 flags; + npy_intp elsize; + npy_intp alignment; + PyObject *metadata; + npy_hash_t hash; + void *reserved_null[2]; +} _PyArray_DescrNumPy2; +#endif /* 1.x and 2.x compatible version */ /* * Semi-private struct with additional field of legacy descriptors (must - * check NPY_DT_is_legacy before casting/accessing). + * check NPY_DT_is_legacy before casting/accessing). The struct is also not + * valid when running on 1.x (i.e. in public API use). */ typedef struct { PyObject_HEAD @@ -674,17 +655,18 @@ typedef struct { char kind; char type; char byteorder; - char flags; + char _former_flags; int type_num; - int elsize; - int alignment; + npy_uint64 flags; + npy_intp elsize; + npy_intp alignment; + PyObject *metadata; + npy_hash_t hash; + void *reserved_null[2]; struct _arr_descr *subarray; PyObject *fields; PyObject *names; - PyArray_ArrFuncs *_former_f; - PyObject *metadata; NpyAuxData *c_metadata; - npy_hash_t hash; } _PyArray_LegacyDescr; @@ -1569,11 +1551,6 @@ PyArray_FLAGS(const PyArrayObject *arr) return ((PyArrayObject_fields *)arr)->flags; } -static inline npy_intp -PyArray_ITEMSIZE(const PyArrayObject *arr) -{ - return ((PyArrayObject_fields *)arr)->descr->elsize; -} static inline int PyArray_TYPE(const PyArrayObject *arr) @@ -1687,42 +1664,13 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags) #define PyDataType_ISUSERDEF(obj) PyTypeNum_ISUSERDEF(((PyArray_Descr*)(obj))->type_num) #define PyDataType_ISEXTENDED(obj) PyTypeNum_ISEXTENDED(((PyArray_Descr*)(obj))->type_num) #define PyDataType_ISOBJECT(obj) PyTypeNum_ISOBJECT(((PyArray_Descr*)(obj))->type_num) -#define PyDataType_HASFIELDS(obj) (PyDataType_ISLEGACY((PyArray_Descr*)(obj)) && ((_PyArray_LegacyDescr *)(obj))->names != NULL) -#define PyDataType_HASSUBARRAY(dtype) (PyDataType_ISLEGACY(dtype) && ((_PyArray_LegacyDescr *)dtype)->subarray != NULL) -#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0 && \ - !PyDataType_HASFIELDS(dtype)) #define PyDataType_MAKEUNSIZED(dtype) ((dtype)->elsize = 0) /* - * PyDataType_FLAGS, PyDataType_FLACHK, and PyDataType_REFCHK require - * npy_2_compat.h and are not defined here. + * PyDataType_* FLAGS, FLACHK, REFCHK, HASFIELDS, HASSUBARRAY, UNSIZED, + * SUBARRAY, NAMES, FIELDS, C_METADATA, and METADATA require version specific + * lookup and are defined in npy_2_compat.h. */ -/* - * Access inline functions for legacy fields. Except metadata these fields are - * specific to structured arrays (names, fields) or datetime (c_metadata). - * Although technically they may be used (but normally ignored) on non-struct - * dtypes as well. - * For structured dtypes, new ways to define and access fields make sense. - */ -static inline PyArray_ArrayDescr * -PyDataType_SUBARRAY(PyArray_Descr *dtype) { - return !PyDataType_ISLEGACY(dtype) ? NULL : ((_PyArray_LegacyDescr *)dtype)->subarray; -} - -static inline PyObject * -PyDataType_NAMES(PyArray_Descr *dtype) { - return !PyDataType_ISLEGACY(dtype) ? NULL : ((_PyArray_LegacyDescr *)dtype)->names; -} - -static inline PyObject * -PyDataType_FIELDS(PyArray_Descr *dtype) { - return !PyDataType_ISLEGACY(dtype) ? NULL : ((_PyArray_LegacyDescr *)dtype)->fields; -} - -static inline NpyAuxData * -PyDataType_C_METADATA(PyArray_Descr *dtype) { - return !PyDataType_ISLEGACY(dtype) ? NULL : ((_PyArray_LegacyDescr *)dtype)->c_metadata; -} #define PyArray_ISBOOL(obj) PyTypeNum_ISBOOL(PyArray_TYPE(obj)) #define PyArray_ISUNSIGNED(obj) PyTypeNum_ISUNSIGNED(PyArray_TYPE(obj)) @@ -1967,13 +1915,4 @@ typedef struct { */ #undef NPY_DEPRECATED_INCLUDES -#if defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD - /* - * we use ndarraytypes.h alone sometimes, but some functions from - * npy_2_compat.h are forward declared here, so ensure we have them. - * (external libraries must eventually include `ndarrayobject.h`) - */ - #include "npy_2_compat.h" -#endif - #endif /* NUMPY_CORE_INCLUDE_NUMPY_NDARRAYTYPES_H_ */ diff --git a/numpy/_core/include/numpy/npy_2_compat.h b/numpy/_core/include/numpy/npy_2_compat.h index a4e545470593..1d6d512f95b5 100644 --- a/numpy/_core/include/numpy/npy_2_compat.h +++ b/numpy/_core/include/numpy/npy_2_compat.h @@ -41,6 +41,7 @@ * In that case we need to ensure that users first included the full headers * and not just `ndarraytypes.h`. */ + #ifndef NPY_FEATURE_VERSION #error "The NumPy 2 compat header requires `import_array()` for which " \ "the `ndarraytypes.h` header include is not sufficient. Please " \ @@ -61,6 +62,8 @@ * This allows downstream to use `PyArray_RUNTIME_VERSION` if they need to. */ #define PyArray_RUNTIME_VERSION NPY_FEATURE_VERSION + /* Compiling on NumPy 1.x where these are the same: */ + #define PyArray_DescrProto PyArray_Descr #endif @@ -107,25 +110,15 @@ PyArray_ImportNumPyAPI() #define NPY_RAVEL_AXIS NPY_MIN_INT #define NPY_MAXARGS 64 - static inline npy_uint64 - PyDataType_FLAGS(const PyArray_Descr *dtype) - { - return (unsigned char)dtype->flags; - } #elif NPY_ABI_VERSION < 0x02000000 #define NPY_DEFAULT_INT NPY_LONG #define NPY_RAVEL_AXIS 32 #define NPY_MAXARGS 32 - static inline npy_uint64 - PyDataType_FLAGS(const PyArray_Descr *dtype) - { - return (unsigned char)dtype->flags; - } - /* Aliases of 2.x names to 1.x only equivalent names */ #define NPY_NTYPES NPY_NTYPES_LEGACY #define PyArray_DescrProto PyArray_Descr + #define _PyArray_LegacyDescr PyArray_Descr /* NumPy 2 definition always works, but add it for 1.x only */ #define PyDataType_ISLEGACY(dtype) (1) #else @@ -135,20 +128,94 @@ PyArray_ImportNumPyAPI() (PyArray_RUNTIME_VERSION >= NPY_2_0_API_VERSION ? -1 : 32) #define NPY_MAXARGS \ (PyArray_RUNTIME_VERSION >= NPY_2_0_API_VERSION ? 64 : 32) +#endif + + +/* + * Access inline functions for descriptor fields. Except for the first + * few fields, these needed to be moved (elsize, alignment) for + * additional space. Or they are descriptor specific and are not generally + * available anymore (metadata, c_metadata, subarray, names, fields). + * + * Most of these are defined via the `DESCR_ACCESSOR` macro helper. + */ +#if NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION || NPY_ABI_VERSION < 0x02000000 + /* Compiling for 1.x or 2.x only, direct field access is OK: */ + + static inline void + PyDataType_SET_ELSIZE(PyArray_Descr *dtype, npy_intp size) + { + dtype->elsize = size; + } + + static inline npy_uint64 + PyDataType_FLAGS(const PyArray_Descr *dtype) + { + #if NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION + return dtype->flags; + #else + return (unsigned char)dtype->flags; /* Need unsigned cast on 1.x */ + #endif + } + + #define DESCR_ACCESSOR(FIELD, field, type, legacy_only) \ + static inline type \ + PyDataType_##FIELD(const PyArray_Descr *dtype) { \ + if (legacy_only && !PyDataType_ISLEGACY(dtype)) { \ + return (type)0; \ + } \ + return ((_PyArray_LegacyDescr *)dtype)->field; \ + } +#else /* compiling for both 1.x and 2.x */ + + static inline void + PyDataType_SET_ELSIZE(PyArray_Descr *dtype, npy_intp size) + { + if (PyArray_RUNTIME_VERSION >= NPY_2_0_API_VERSION) { + ((_PyArray_DescrNumPy2 *)dtype)->elsize = size; + } + else { + ((PyArray_DescrProto *)dtype)->elsize = (int)size; + } + } static inline npy_uint64 PyDataType_FLAGS(const PyArray_Descr *dtype) { if (PyArray_RUNTIME_VERSION >= NPY_2_0_API_VERSION) { - // TODO: This will change to a semi-private 2.0 struct name - return (unsigned char)((PyArray_Descr *)dtype)->flags; + return ((_PyArray_DescrNumPy2 *)dtype)->flags; } else { return (unsigned char)((PyArray_DescrProto *)dtype)->flags; } } + + /* Cast to LegacyDescr always fine but needed when `legacy_only` */ + #define DESCR_ACCESSOR(FIELD, field, type, legacy_only) \ + static inline type \ + PyDataType_##FIELD(const PyArray_Descr *dtype) { \ + if (legacy_only && !PyDataType_ISLEGACY(dtype)) { \ + return (type)0; \ + } \ + if (PyArray_RUNTIME_VERSION >= NPY_2_0_API_VERSION) { \ + return ((_PyArray_LegacyDescr *)dtype)->field; \ + } \ + else { \ + return ((PyArray_DescrProto *)dtype)->field; \ + } \ + } #endif +DESCR_ACCESSOR(ELSIZE, elsize, npy_intp, 0) +DESCR_ACCESSOR(ALIGNMENT, alignment, npy_intp, 0) +DESCR_ACCESSOR(METADATA, metadata, PyObject *, 1) +DESCR_ACCESSOR(SUBARRAY, subarray, PyArray_ArrayDescr *, 1) +DESCR_ACCESSOR(NAMES, names, PyObject *, 1) +DESCR_ACCESSOR(FIELDS, fields, PyObject *, 1) +DESCR_ACCESSOR(C_METADATA, c_metadata, NpyAuxData *, 1) + +#undef DESCR_ACCESSOR + #if !(defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD) #if NPY_FEATURE_VERSION >= NPY_2_0_API_VERSION diff --git a/numpy/_core/src/common/mem_overlap.c b/numpy/_core/src/common/mem_overlap.c index 2632e1413f48..d5b734cad120 100644 --- a/numpy/_core/src/common/mem_overlap.c +++ b/numpy/_core/src/common/mem_overlap.c @@ -186,7 +186,7 @@ #define PY_SSIZE_T_CLEAN #include -#include "numpy/ndarraytypes.h" +#include "numpy/ndarrayobject.h" #include "mem_overlap.h" #include "npy_extint128.h" diff --git a/numpy/_core/src/multiarray/arraytypes.c.src b/numpy/_core/src/multiarray/arraytypes.c.src index ad6414aae8f2..536d98e49707 100644 --- a/numpy/_core/src/multiarray/arraytypes.c.src +++ b/numpy/_core/src/multiarray/arraytypes.c.src @@ -1125,7 +1125,7 @@ VOID_setitem(PyObject *op, void *input, void *vap) } return 0; } - else if (PyDataType_HASSUBARRAY(descr)) { + else if (descr->subarray != NULL) { /* copy into an array of the same basic type */ PyArray_Dims shape = {NULL, -1}; if (!(PyArray_IntpConverter(descr->subarray->shape, &shape))) { @@ -2297,7 +2297,7 @@ VOID_copyswapn (char *dst, npy_intp dstride, char *src, npy_intp sstride, } return; } - if (PyDataType_HASSUBARRAY(descr)) { + if (descr->subarray) { PyArray_Descr *new; npy_intp num; npy_intp i; @@ -2382,7 +2382,7 @@ VOID_copyswap (char *dst, char *src, int swap, PyArrayObject *arr) } return; } - if (PyDataType_HASSUBARRAY(descr)) { + if (descr->subarray != NULL) { PyArray_Descr *new; npy_intp num; int subitemsize; @@ -4048,7 +4048,6 @@ static _PyArray_LegacyDescr @from@_Descr = { .type_num = NPY_@from@, .elsize = 0, .alignment = NPY_ALIGNOF(@align@), - ._former_f = &_Py@NAME@_ArrFuncs, .hash = -1, }; @@ -4178,7 +4177,6 @@ NPY_NO_EXPORT _PyArray_LegacyDescr @from@_Descr = { .type_num = NPY_@from@, .elsize = sizeof(@fromtype@), .alignment = NPY_ALIGNOF(@fromtype@), - ._former_f = &_Py@NAME@_ArrFuncs, .hash = -1, }; diff --git a/numpy/_core/src/multiarray/buffer.c b/numpy/_core/src/multiarray/buffer.c index a0931d691649..f83e7b918e4e 100644 --- a/numpy/_core/src/multiarray/buffer.c +++ b/numpy/_core/src/multiarray/buffer.c @@ -401,7 +401,7 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, case NPY_OBJECT: if (_append_char(str, 'O') < 0) return -1; break; case NPY_STRING: { char buf[128]; - PyOS_snprintf(buf, sizeof(buf), "%ds", descr->elsize); + PyOS_snprintf(buf, sizeof(buf), "%" NPY_INTP_FMT "s", descr->elsize); if (_append_str(str, buf) < 0) return -1; break; } @@ -409,14 +409,14 @@ _buffer_format_string(PyArray_Descr *descr, _tmp_string_t *str, /* NumPy Unicode is always 4-byte */ char buf[128]; assert(descr->elsize % 4 == 0); - PyOS_snprintf(buf, sizeof(buf), "%dw", descr->elsize / 4); + PyOS_snprintf(buf, sizeof(buf), "%" NPY_INTP_FMT "w", descr->elsize / 4); if (_append_str(str, buf) < 0) return -1; break; } case NPY_VOID: { /* Insert padding bytes */ char buf[128]; - PyOS_snprintf(buf, sizeof(buf), "%dx", descr->elsize); + PyOS_snprintf(buf, sizeof(buf), "%" NPY_INTP_FMT "x", descr->elsize); if (_append_str(str, buf) < 0) return -1; break; } diff --git a/numpy/_core/src/multiarray/descriptor.c b/numpy/_core/src/multiarray/descriptor.c index cc42a2d515b4..c77b380e9386 100644 --- a/numpy/_core/src/multiarray/descriptor.c +++ b/numpy/_core/src/multiarray/descriptor.c @@ -1969,7 +1969,7 @@ PyArray_DescrNew(PyArray_Descr *base_descr) /* Don't copy PyObject_HEAD part */ memcpy((char *)newdescr + sizeof(PyObject), (char *)base + sizeof(PyObject), - sizeof(PyArray_Descr) - sizeof(PyObject)); + sizeof(_PyArray_LegacyDescr) - sizeof(PyObject)); /* * The c_metadata has a by-value ownership model, need to clone it @@ -2063,12 +2063,16 @@ static PyMemberDef arraydescr_members[] = { {"byteorder", T_CHAR, offsetof(PyArray_Descr, byteorder), READONLY, NULL}, {"itemsize", - T_INT, offsetof(PyArray_Descr, elsize), READONLY, NULL}, + T_PYSSIZET, offsetof(PyArray_Descr, elsize), READONLY, NULL}, {"alignment", - T_INT, offsetof(PyArray_Descr, alignment), READONLY, NULL}, + T_PYSSIZET, offsetof(PyArray_Descr, alignment), READONLY, NULL}, {"flags", - T_BYTE, offsetof(PyArray_Descr, flags), READONLY, NULL}, - {NULL, 0, 0, 0, NULL}, +#if NPY_ULONGLONG == NPY_UINT64 + T_ULONGLONG, offsetof(PyArray_Descr, flags), READONLY, NULL}, +#else + #error Assuming long long is 64bit, if not replace with getter function. +#endif + {NULL, 0, 0, 0, NULL}, }; static PyObject * @@ -2785,7 +2789,7 @@ arraydescr_reduce(PyArray_Descr *self, PyObject *NPY_UNUSED(args)) } PyTuple_SET_ITEM(state, 5, PyLong_FromLong(elsize)); PyTuple_SET_ITEM(state, 6, PyLong_FromLong(alignment)); - PyTuple_SET_ITEM(state, 7, PyLong_FromLong(self->flags)); + PyTuple_SET_ITEM(state, 7, PyLong_FromUnsignedLongLong(self->flags)); PyTuple_SET_ITEM(ret, 2, state); return ret; @@ -2840,7 +2844,7 @@ arraydescr_setstate(_PyArray_LegacyDescr *self, PyObject *args) PyObject *subarray, *fields, *names = NULL, *metadata=NULL; int incref_names = 1; int int_dtypeflags = 0; - char dtypeflags; + npy_uint64 dtypeflags; if (!PyDataType_ISLEGACY(self)) { PyErr_SetString(PyExc_RuntimeError, @@ -3043,7 +3047,7 @@ arraydescr_setstate(_PyArray_LegacyDescr *self, PyObject *args) } self->subarray = PyArray_malloc(sizeof(PyArray_ArrayDescr)); - if (!PyDataType_HASSUBARRAY(self)) { + if (self->subarray == NULL) { return PyErr_NoMemory(); } self->subarray->base = (PyArray_Descr *)PyTuple_GET_ITEM(subarray, 0); @@ -3140,6 +3144,10 @@ arraydescr_setstate(_PyArray_LegacyDescr *self, PyObject *args) * flags as an int even though it actually was a char in the PyArray_Descr * structure */ + if (int_dtypeflags < 0 && int_dtypeflags >= -128) { + /* NumPy used to use a char. So normalize if signed. */ + int_dtypeflags += 128; + } dtypeflags = int_dtypeflags; if (dtypeflags != int_dtypeflags) { PyErr_Format(PyExc_ValueError, @@ -3337,7 +3345,7 @@ PyArray_DescrNewByteorder(PyArray_Descr *oself, char newendian) Py_DECREF(new->fields); new->fields = newfields; } - if (PyDataType_HASSUBARRAY(new)) { + if (new->subarray) { Py_DECREF(new->subarray->base); new->subarray->base = PyArray_DescrNewByteorder( self->subarray->base, newendian); diff --git a/numpy/_core/src/multiarray/dtype_traversal.c b/numpy/_core/src/multiarray/dtype_traversal.c index 7cf1e0d144e5..0402ad2c084d 100644 --- a/numpy/_core/src/multiarray/dtype_traversal.c +++ b/numpy/_core/src/multiarray/dtype_traversal.c @@ -516,7 +516,7 @@ npy_get_clear_void_and_legacy_user_dtype_loop( return 0; } - if (PyDataType_HASSUBARRAY(dtype)) { + if (dtype->subarray != NULL) { PyArray_Dims shape = {NULL, -1}; npy_intp size; @@ -602,7 +602,7 @@ npy_get_zerofill_void_and_legacy_user_dtype_loop( npy_intp stride, PyArrayMethod_TraverseLoop **out_func, NpyAuxData **out_auxdata, NPY_ARRAYMETHOD_FLAGS *flags) { - if (PyDataType_HASSUBARRAY(dtype)) { + if (dtype->subarray != NULL) { PyArray_Dims shape = {NULL, -1}; npy_intp size; diff --git a/numpy/_core/src/multiarray/dtypemeta.c b/numpy/_core/src/multiarray/dtypemeta.c index 32c867d8c150..626b3bde1032 100644 --- a/numpy/_core/src/multiarray/dtypemeta.c +++ b/numpy/_core/src/multiarray/dtypemeta.c @@ -1125,7 +1125,7 @@ dtypemeta_wrap_legacy_descriptor(_PyArray_LegacyDescr *descr, {{ PyVarObject_HEAD_INIT(&PyArrayDTypeMeta_Type, 0) .tp_name = NULL, /* set below */ - .tp_basicsize = sizeof(PyArray_Descr), + .tp_basicsize = sizeof(_PyArray_LegacyDescr), .tp_flags = Py_TPFLAGS_DEFAULT, .tp_base = &PyArrayDescr_Type, .tp_new = (newfunc)legacy_dtype_default_new, diff --git a/numpy/_core/src/multiarray/hashdescr.c b/numpy/_core/src/multiarray/hashdescr.c index 1ec396c6e67f..925179e30a53 100644 --- a/numpy/_core/src/multiarray/hashdescr.c +++ b/numpy/_core/src/multiarray/hashdescr.c @@ -244,7 +244,7 @@ static int _array_descr_walk(PyArray_Descr* descr, PyObject *l) return -1; } } - if(PyDataType_HASSUBARRAY(ldescr)) { + if(ldescr->subarray != NULL) { st = _array_descr_walk_subarray(ldescr->subarray, l); if (st) { return -1; diff --git a/numpy/_core/src/multiarray/usertypes.c b/numpy/_core/src/multiarray/usertypes.c index 322c90f3fdc6..92325247a60c 100644 --- a/numpy/_core/src/multiarray/usertypes.c +++ b/numpy/_core/src/multiarray/usertypes.c @@ -268,7 +268,7 @@ PyArray_RegisterDataType(PyArray_DescrProto *descr_proto) * Copy the user provided descriptor struct into a new one. This is done * in order to allow different layout between the two. */ - _PyArray_LegacyDescr *descr = PyObject_Malloc(sizeof(PyArray_Descr)); + _PyArray_LegacyDescr *descr = PyObject_Malloc(sizeof(_PyArray_LegacyDescr)); if (descr == NULL) { PyMem_FREE(name); PyErr_NoMemory(); diff --git a/numpy/f2py/src/fortranobject.c b/numpy/f2py/src/fortranobject.c index 81bb035c8ff6..3594147281a2 100644 --- a/numpy/f2py/src/fortranobject.c +++ b/numpy/f2py/src/fortranobject.c @@ -111,7 +111,7 @@ get_descr_from_type_and_elsize(const int type_num, const int elsize) { if (descr == NULL) { return NULL; } - descr->elsize = elsize; + PyDataType_SET_ELSIZE(descr, elsize); } return descr; } @@ -904,7 +904,7 @@ ndarray_from_pyobj(const int type_num, if (descr == NULL) { return NULL; } - elsize = descr->elsize; + elsize = PyDataType_ELSIZE(descr); if ((intent & F2PY_INTENT_HIDE) || ((intent & F2PY_INTENT_CACHE) && (obj == Py_None)) || ((intent & F2PY_OPTIONAL) && (obj == Py_None)) @@ -1110,7 +1110,7 @@ array_from_pyobj(const int type_num, if possible. Provided for backward compatibility. */ PyArray_Descr* descr = PyArray_DescrFromType(type_num); - int elsize = descr->elsize; + int elsize = PyDataType_ELSIZE(descr); Py_DECREF(descr); return ndarray_from_pyobj(type_num, elsize, dims, rank, intent, obj, NULL); }