diff options
author | Nathan Goldbaum <nathan.goldbaum@gmail.com> | 2023-03-10 08:21:19 -0700 |
---|---|---|
committer | Nathan Goldbaum <nathan.goldbaum@gmail.com> | 2023-03-17 09:52:37 -0600 |
commit | a24e785ead6dbd80050cb157326a6a23b279d4e4 (patch) | |
tree | b2b941a4fa915dfda6b2f8ff6def671eafd8cb4c /numpy | |
parent | e42c9503a14d66adfd41356ef5640c6975c45218 (diff) | |
download | numpy-a24e785ead6dbd80050cb157326a6a23b279d4e4.tar.gz |
ENH: allow using dtype classes in array creation functions
This enables writing np.array(some_object, dtype=type(np.dtype('i'))). This
is a follow-on from https://github.com/numpy/numpy/pull/23154, see that PR
for more details.
I had to add a new include to `ctors.h` to bring in the definition of the
`npy_dtype_info` struct. Since `ctors.h` is included in many other files inside
numpy, I found that I needed to modify fewer includes across numpy if I moved
the definition of `npy_dtype_info` to `common.h` from `descriptor.h`. The new
includes of `common.h` are needed to support later includes of `ctors.h` in
those files. If anyone has an alternate place to put `npy_dtype_info` that would
cause less churn of includes I'd love to hear about it.
I spent a bunch of time tweaking the reference counts. I'm reasonably confident
this is correct but not 100%, an additional careful pass over the reference
count logic from a reviewer would be very appreciated.
I could have made `_PyArray_FromAny` and `_PyArray_CheckFromAny` take just a
`npy_dtype_info` struct, but I found it made the reference count logic more
complicated, since `PyArray_FromAny` and `PyArray_CheckFromAny` steal the
reference to the descriptor they are passed and I needed to conserve that
behavior. Also both functions support passing in a `NULL` pointer for the
descriptor and I needed to maintain that behavior as well.
The change to `ucsnarrow.h` fixes a preexisting conflict with the prototype
in `ucsnarrow.c` that triggered a compiler error while I was working on this.
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/common/ucsnarrow.c | 1 | ||||
-rw-r--r-- | numpy/core/src/common/ucsnarrow.h | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/array_coercion.c | 9 | ||||
-rw-r--r-- | numpy/core/src/multiarray/common.h | 12 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 68 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.h | 9 | ||||
-rw-r--r-- | numpy/core/src/multiarray/descriptor.h | 12 | ||||
-rw-r--r-- | numpy/core/src/multiarray/iterators.c | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 107 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalarapi.c | 1 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 1 | ||||
-rw-r--r-- | numpy/core/src/multiarray/shape.c | 1 | ||||
-rw-r--r-- | numpy/core/src/umath/reduction.c | 1 | ||||
-rw-r--r-- | numpy/core/tests/test_custom_dtypes.py | 6 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 11 |
15 files changed, 173 insertions, 70 deletions
diff --git a/numpy/core/src/common/ucsnarrow.c b/numpy/core/src/common/ucsnarrow.c index 4bea4beee..adf441a12 100644 --- a/numpy/core/src/common/ucsnarrow.c +++ b/numpy/core/src/common/ucsnarrow.c @@ -10,6 +10,7 @@ #include "npy_config.h" #include "npy_pycompat.h" +#include "common.h" #include "ctors.h" /* diff --git a/numpy/core/src/common/ucsnarrow.h b/numpy/core/src/common/ucsnarrow.h index 6fe157199..4b17a2809 100644 --- a/numpy/core/src/common/ucsnarrow.h +++ b/numpy/core/src/common/ucsnarrow.h @@ -2,6 +2,6 @@ #define NUMPY_CORE_SRC_COMMON_NPY_UCSNARROW_H_ NPY_NO_EXPORT PyUnicodeObject * -PyUnicode_FromUCS4(char *src, Py_ssize_t size, int swap, int align); +PyUnicode_FromUCS4(char const *src, Py_ssize_t size, int swap, int align); #endif /* NUMPY_CORE_SRC_COMMON_NPY_UCSNARROW_H_ */ diff --git a/numpy/core/src/multiarray/array_coercion.c b/numpy/core/src/multiarray/array_coercion.c index d6bee1a7b..d55a5752b 100644 --- a/numpy/core/src/multiarray/array_coercion.c +++ b/numpy/core/src/multiarray/array_coercion.c @@ -878,7 +878,8 @@ find_descriptor_from_array( * means that legacy behavior is used: The dtype instances "S0", "U0", and * "V0" are converted to mean the DType classes instead. * When dtype != NULL, this path is ignored, and the function does nothing - * unless descr == NULL. + * unless descr == NULL. If both descr and dtype are null, it returns the + * descriptor for the array. * * This function is identical to normal casting using only the dtype, however, * it supports inspecting the elements when the array has object dtype @@ -927,7 +928,7 @@ PyArray_AdaptDescriptorToArray( /* This is an object array but contained no elements, use default */ new_descr = NPY_DT_CALL_default_descr(dtype); } - Py_DECREF(dtype); + Py_XDECREF(dtype); return new_descr; } @@ -1280,7 +1281,9 @@ PyArray_DiscoverDTypeAndShape( } if (requested_descr != NULL) { - assert(fixed_DType == NPY_DTYPE(requested_descr)); + if (fixed_DType != NULL) { + assert(fixed_DType == NPY_DTYPE(requested_descr)); + } /* The output descriptor must be the input. */ Py_INCREF(requested_descr); *out_descr = requested_descr; diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h index 4e067b22c..506bf4fef 100644 --- a/numpy/core/src/multiarray/common.h +++ b/numpy/core/src/multiarray/common.h @@ -360,4 +360,16 @@ new_array_for_sum(PyArrayObject *ap1, PyArrayObject *ap2, PyArrayObject* out, */ #define NPY_ITER_REDUCTION_AXIS(axis) (axis + (1 << (NPY_BITSOF_INT - 2))) +/* + * In some API calls we wish to allow users to pass a DType class or a + * dtype instances with different meanings. + * This struct is mainly used for the argument parsing in + * `PyArray_DTypeOrDescrConverter`. + */ +typedef struct { + PyArray_DTypeMeta *dtype; + PyArray_Descr *descr; +} npy_dtype_info; + + #endif /* NUMPY_CORE_SRC_MULTIARRAY_COMMON_H_ */ diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 38af60427..d5b599812 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -1605,6 +1605,31 @@ NPY_NO_EXPORT PyObject * PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth, int max_depth, int flags, PyObject *context) { + npy_dtype_info dt_info = {NULL, NULL}; + + int res = PyArray_ExtractDTypeAndDescriptor( + newtype, &dt_info.descr, &dt_info.dtype); + + if (res < 0) { + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); + return NULL; + } + + PyObject* ret = _PyArray_FromAny(op, newtype, dt_info, min_depth, + max_depth, flags, context); + + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); + return ret; +} + +// private version updated to accept npy_dtype_info + +NPY_NO_EXPORT PyObject * +_PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, npy_dtype_info dt_info, + int min_depth, int max_depth, int flags, PyObject *context) +{ /* * This is the main code to make a NumPy array from a Python * Object. It is called from many different places. @@ -1620,26 +1645,18 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth, return NULL; } - PyArray_Descr *fixed_descriptor; - PyArray_DTypeMeta *fixed_DType; - if (PyArray_ExtractDTypeAndDescriptor(newtype, - &fixed_descriptor, &fixed_DType) < 0) { - Py_XDECREF(newtype); - return NULL; - } + // steal reference Py_XDECREF(newtype); ndim = PyArray_DiscoverDTypeAndShape(op, - NPY_MAXDIMS, dims, &cache, fixed_DType, fixed_descriptor, &dtype, + NPY_MAXDIMS, dims, &cache, dt_info.dtype, dt_info.descr, &dtype, flags & NPY_ARRAY_ENSURENOCOPY); - Py_XDECREF(fixed_descriptor); - Py_XDECREF(fixed_DType); if (ndim < 0) { return NULL; } - if (NPY_UNLIKELY(fixed_descriptor != NULL && PyDataType_HASSUBARRAY(dtype))) { + if (NPY_UNLIKELY(dt_info.descr != NULL && PyDataType_HASSUBARRAY(dtype))) { /* * When a subarray dtype was passed in, its dimensions are appended * to the array dimension (causing a dimension mismatch). @@ -1909,6 +1926,32 @@ NPY_NO_EXPORT PyObject * PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, int min_depth, int max_depth, int requires, PyObject *context) { + npy_dtype_info dt_info = {NULL, NULL}; + + int res = PyArray_ExtractDTypeAndDescriptor( + descr, &dt_info.descr, &dt_info.dtype); + + if (res < 0) { + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); + return NULL; + } + + PyObject* ret = _PyArray_CheckFromAny(op, descr, dt_info, min_depth, + max_depth, requires, context); + + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); + return ret; +} + +// private version updated to accept npy_dtype_info + +NPY_NO_EXPORT PyObject * +_PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, + npy_dtype_info dt_info, int min_depth, + int max_depth, int requires, PyObject *context) +{ PyObject *obj; if (requires & NPY_ARRAY_NOTSWAPPED) { if (!descr && PyArray_Check(op) && @@ -1926,7 +1969,8 @@ PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, int min_depth, } } - obj = PyArray_FromAny(op, descr, min_depth, max_depth, requires, context); + obj = _PyArray_FromAny(op, descr, dt_info, min_depth, max_depth, requires, + context); if (obj == NULL) { return NULL; } diff --git a/numpy/core/src/multiarray/ctors.h b/numpy/core/src/multiarray/ctors.h index 98160b1cc..2b8ab0e0e 100644 --- a/numpy/core/src/multiarray/ctors.h +++ b/numpy/core/src/multiarray/ctors.h @@ -36,10 +36,19 @@ _array_from_array_like(PyObject *op, int never_copy); NPY_NO_EXPORT PyObject * +_PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, npy_dtype_info dt_info, + int min_depth, int max_depth, int flags, PyObject *context); + +NPY_NO_EXPORT PyObject * PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth, int max_depth, int flags, PyObject *context); NPY_NO_EXPORT PyObject * +_PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, + npy_dtype_info dt_info, int min_depth, + int max_depth, int requires, PyObject *context); + +NPY_NO_EXPORT PyObject * PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, int min_depth, int max_depth, int requires, PyObject *context); diff --git a/numpy/core/src/multiarray/descriptor.h b/numpy/core/src/multiarray/descriptor.h index b14edc3aa..9f7d2a2fd 100644 --- a/numpy/core/src/multiarray/descriptor.h +++ b/numpy/core/src/multiarray/descriptor.h @@ -2,18 +2,6 @@ #define NUMPY_CORE_SRC_MULTIARRAY_DESCRIPTOR_H_ -/* - * In some API calls we wish to allow users to pass a DType class or a - * dtype instances with different meanings. - * This struct is mainly used for the argument parsing in - * `PyArray_DTypeOrDescrConverter`. - */ -typedef struct { - PyArray_DTypeMeta *dtype; - PyArray_Descr *descr; -} npy_dtype_info; - - NPY_NO_EXPORT int PyArray_DTypeOrDescrConverterOptional(PyObject *, npy_dtype_info *dt_info); diff --git a/numpy/core/src/multiarray/iterators.c b/numpy/core/src/multiarray/iterators.c index 4e6418bf9..8a4027bfc 100644 --- a/numpy/core/src/multiarray/iterators.c +++ b/numpy/core/src/multiarray/iterators.c @@ -14,8 +14,8 @@ #include "arrayobject.h" #include "iterators.h" -#include "ctors.h" #include "common.h" +#include "ctors.h" #include "conversion_utils.h" #include "array_coercion.h" diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index e85f8affa..e4b8cb8df 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -1637,8 +1637,8 @@ _prepend_ones(PyArrayObject *arr, int nd, int ndmin, NPY_ORDER order) static inline PyObject * _array_fromobject_generic( - PyObject *op, PyArray_Descr *type, _PyArray_CopyMode copy, NPY_ORDER order, - npy_bool subok, int ndmin) + PyObject *op, npy_dtype_info dt_info, _PyArray_CopyMode copy, + NPY_ORDER order, npy_bool subok, int ndmin) { PyArrayObject *oparr = NULL, *ret = NULL; PyArray_Descr *oldtype = NULL; @@ -1653,7 +1653,9 @@ _array_fromobject_generic( /* fast exit if simple call */ if (PyArray_CheckExact(op) || (subok && PyArray_Check(op))) { oparr = (PyArrayObject *)op; - if (type == NULL) { + PyArray_Descr* dtype = PyArray_AdaptDescriptorToArray( + oparr, dt_info.dtype, dt_info.descr); + if (dtype == NULL) { if (copy != NPY_COPY_ALWAYS && STRIDING_OK(oparr, order)) { ret = oparr; Py_INCREF(ret); @@ -1671,9 +1673,9 @@ _array_fromobject_generic( } /* One more chance for faster exit if user specified the dtype. */ oldtype = PyArray_DESCR(oparr); - if (PyArray_EquivTypes(oldtype, type)) { + if (PyArray_EquivTypes(oldtype, dtype)) { if (copy != NPY_COPY_ALWAYS && STRIDING_OK(oparr, order)) { - if (oldtype == type) { + if (oldtype == dtype) { Py_INCREF(op); ret = oparr; } @@ -1681,10 +1683,10 @@ _array_fromobject_generic( /* Create a new PyArrayObject from the caller's * PyArray_Descr. Use the reference `op` as the base * object. */ - Py_INCREF(type); + Py_INCREF(dtype); ret = (PyArrayObject *)PyArray_NewFromDescrAndBase( Py_TYPE(op), - type, + dtype, PyArray_NDIM(oparr), PyArray_DIMS(oparr), PyArray_STRIDES(oparr), @@ -1694,24 +1696,31 @@ _array_fromobject_generic( op ); } + Py_DECREF(dtype); goto finish; } else { if (copy == NPY_COPY_NEVER) { PyErr_SetString(PyExc_ValueError, "Unable to avoid copy while creating a new array."); + Py_DECREF(dtype); return NULL; } ret = (PyArrayObject *)PyArray_NewCopy(oparr, order); - if (oldtype == type || ret == NULL) { + if (oldtype == dtype || ret == NULL) { + Py_DECREF(dtype); goto finish; } Py_INCREF(oldtype); Py_DECREF(PyArray_DESCR(ret)); + Py_DECREF(dtype); ((PyArrayObject_fields *)ret)->descr = oldtype; goto finish; } } + else { + Py_DECREF(dtype); + } } if (copy == NPY_COPY_ALWAYS) { @@ -1734,9 +1743,10 @@ _array_fromobject_generic( } flags |= NPY_ARRAY_FORCECAST; - Py_XINCREF(type); - ret = (PyArrayObject *)PyArray_CheckFromAny(op, type, - 0, 0, flags, NULL); + + Py_XINCREF(dt_info.descr); + ret = (PyArrayObject *)_PyArray_CheckFromAny(op, dt_info.descr, dt_info, + 0, 0, flags, NULL); finish: if (ret == NULL) { @@ -1765,7 +1775,7 @@ array_array(PyObject *NPY_UNUSED(ignored), npy_bool subok = NPY_FALSE; _PyArray_CopyMode copy = NPY_COPY_ALWAYS; int ndmin = 0; - PyArray_Descr *type = NULL; + npy_dtype_info dt_info = {NULL, NULL}; NPY_ORDER order = NPY_KEEPORDER; PyObject *like = Py_None; NPY_PREPARE_ARGPARSER; @@ -1773,21 +1783,23 @@ array_array(PyObject *NPY_UNUSED(ignored), if (len_args != 1 || (kwnames != NULL)) { if (npy_parse_arguments("array", args, len_args, kwnames, "object", NULL, &op, - "|dtype", &PyArray_DescrConverter2, &type, + "|dtype", &PyArray_DTypeOrDescrConverterOptional, &dt_info, "$copy", &PyArray_CopyConverter, ©, "$order", &PyArray_OrderConverter, &order, "$subok", &PyArray_BoolConverter, &subok, "$ndmin", &PyArray_PythonPyIntFromInt, &ndmin, "$like", NULL, &like, NULL, NULL, NULL) < 0) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return NULL; } if (like != Py_None) { PyObject *deferred = array_implement_c_array_function_creation( "array", like, NULL, NULL, args, len_args, kwnames); if (deferred != Py_NotImplemented) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return deferred; } } @@ -1798,8 +1810,9 @@ array_array(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, type, copy, order, subok, ndmin); - Py_XDECREF(type); + op, dt_info, copy, order, subok, ndmin); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return res; } @@ -1808,7 +1821,7 @@ array_asarray(PyObject *NPY_UNUSED(ignored), PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *op; - PyArray_Descr *type = NULL; + npy_dtype_info dt_info = {NULL, NULL}; NPY_ORDER order = NPY_KEEPORDER; PyObject *like = Py_None; NPY_PREPARE_ARGPARSER; @@ -1816,18 +1829,20 @@ array_asarray(PyObject *NPY_UNUSED(ignored), if (len_args != 1 || (kwnames != NULL)) { if (npy_parse_arguments("asarray", args, len_args, kwnames, "a", NULL, &op, - "|dtype", &PyArray_DescrConverter2, &type, + "|dtype", &PyArray_DTypeOrDescrConverterOptional, &dt_info, "|order", &PyArray_OrderConverter, &order, "$like", NULL, &like, NULL, NULL, NULL) < 0) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return NULL; } if (like != Py_None) { PyObject *deferred = array_implement_c_array_function_creation( "asarray", like, NULL, NULL, args, len_args, kwnames); if (deferred != Py_NotImplemented) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return deferred; } } @@ -1837,8 +1852,9 @@ array_asarray(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, type, NPY_FALSE, order, NPY_FALSE, 0); - Py_XDECREF(type); + op, dt_info, NPY_FALSE, order, NPY_FALSE, 0); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return res; } @@ -1847,7 +1863,7 @@ array_asanyarray(PyObject *NPY_UNUSED(ignored), PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *op; - PyArray_Descr *type = NULL; + npy_dtype_info dt_info = {NULL, NULL}; NPY_ORDER order = NPY_KEEPORDER; PyObject *like = Py_None; NPY_PREPARE_ARGPARSER; @@ -1855,18 +1871,20 @@ array_asanyarray(PyObject *NPY_UNUSED(ignored), if (len_args != 1 || (kwnames != NULL)) { if (npy_parse_arguments("asanyarray", args, len_args, kwnames, "a", NULL, &op, - "|dtype", &PyArray_DescrConverter2, &type, + "|dtype", &PyArray_DTypeOrDescrConverterOptional, &dt_info, "|order", &PyArray_OrderConverter, &order, "$like", NULL, &like, NULL, NULL, NULL) < 0) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return NULL; } if (like != Py_None) { PyObject *deferred = array_implement_c_array_function_creation( "asanyarray", like, NULL, NULL, args, len_args, kwnames); if (deferred != Py_NotImplemented) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return deferred; } } @@ -1876,8 +1894,9 @@ array_asanyarray(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, type, NPY_FALSE, order, NPY_TRUE, 0); - Py_XDECREF(type); + op, dt_info, NPY_FALSE, order, NPY_TRUE, 0); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return res; } @@ -1887,24 +1906,26 @@ array_ascontiguousarray(PyObject *NPY_UNUSED(ignored), PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *op; - PyArray_Descr *type = NULL; + npy_dtype_info dt_info = {NULL, NULL}; PyObject *like = Py_None; NPY_PREPARE_ARGPARSER; if (len_args != 1 || (kwnames != NULL)) { if (npy_parse_arguments("ascontiguousarray", args, len_args, kwnames, "a", NULL, &op, - "|dtype", &PyArray_DescrConverter2, &type, + "|dtype", &PyArray_DTypeOrDescrConverterOptional, &dt_info, "$like", NULL, &like, NULL, NULL, NULL) < 0) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return NULL; } if (like != Py_None) { PyObject *deferred = array_implement_c_array_function_creation( "ascontiguousarray", like, NULL, NULL, args, len_args, kwnames); if (deferred != Py_NotImplemented) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return deferred; } } @@ -1914,8 +1935,9 @@ array_ascontiguousarray(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, type, NPY_FALSE, NPY_CORDER, NPY_FALSE, 1); - Py_XDECREF(type); + op, dt_info, NPY_FALSE, NPY_CORDER, NPY_FALSE, 1); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return res; } @@ -1925,24 +1947,26 @@ array_asfortranarray(PyObject *NPY_UNUSED(ignored), PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames) { PyObject *op; - PyArray_Descr *type = NULL; + npy_dtype_info dt_info = {NULL, NULL}; PyObject *like = Py_None; NPY_PREPARE_ARGPARSER; if (len_args != 1 || (kwnames != NULL)) { if (npy_parse_arguments("asfortranarray", args, len_args, kwnames, "a", NULL, &op, - "|dtype", &PyArray_DescrConverter2, &type, + "|dtype", &PyArray_DTypeOrDescrConverterOptional, &dt_info, "$like", NULL, &like, NULL, NULL, NULL) < 0) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return NULL; } if (like != Py_None) { PyObject *deferred = array_implement_c_array_function_creation( "asfortranarray", like, NULL, NULL, args, len_args, kwnames); if (deferred != Py_NotImplemented) { - Py_XDECREF(type); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return deferred; } } @@ -1952,8 +1976,9 @@ array_asfortranarray(PyObject *NPY_UNUSED(ignored), } PyObject *res = _array_fromobject_generic( - op, type, NPY_FALSE, NPY_FORTRANORDER, NPY_FALSE, 1); - Py_XDECREF(type); + op, dt_info, NPY_FALSE, NPY_FORTRANORDER, NPY_FALSE, 1); + Py_XDECREF(dt_info.descr); + Py_XDECREF(dt_info.dtype); return res; } diff --git a/numpy/core/src/multiarray/scalarapi.c b/numpy/core/src/multiarray/scalarapi.c index 40f1da2c4..62479eabb 100644 --- a/numpy/core/src/multiarray/scalarapi.c +++ b/numpy/core/src/multiarray/scalarapi.c @@ -14,6 +14,7 @@ #include "npy_pycompat.h" +#include "common.h" #include "ctors.h" #include "descriptor.h" #include "scalartypes.h" diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index 17163ef09..51822276e 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -17,6 +17,7 @@ #include "npy_config.h" #include "mapping.h" +#include "common.h" #include "ctors.h" #include "usertypes.h" #include "numpyos.h" diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c index 25af43bbc..1ae314bad 100644 --- a/numpy/core/src/multiarray/shape.c +++ b/numpy/core/src/multiarray/shape.c @@ -14,6 +14,7 @@ #include "npy_pycompat.h" +#include "common.h" #include "ctors.h" #include "shape.h" diff --git a/numpy/core/src/umath/reduction.c b/numpy/core/src/umath/reduction.c index 9416e9a29..188039e1a 100644 --- a/numpy/core/src/umath/reduction.c +++ b/numpy/core/src/umath/reduction.c @@ -20,6 +20,7 @@ #include "array_assign.h" #include "array_coercion.h" #include "array_method.h" +#include "common.h" #include "ctors.h" #include "numpy/ufuncobject.h" diff --git a/numpy/core/tests/test_custom_dtypes.py b/numpy/core/tests/test_custom_dtypes.py index 1a34c6fa3..da6a4bd50 100644 --- a/numpy/core/tests/test_custom_dtypes.py +++ b/numpy/core/tests/test_custom_dtypes.py @@ -229,6 +229,12 @@ class TestSFloat: expected = arr.astype(SF(1.)) # above will have discovered 1. scaling assert_array_equal(res.view(np.float64), expected.view(np.float64)) + def test_creation_class(self): + arr1 = np.array([1., 2., 3.], dtype=SF) + assert arr1.dtype == SF(1.) + arr2 = np.array([1., 2., 3.], dtype=SF(1.)) + assert_array_equal(arr1.view(np.float64), arr2.view(np.float64)) + def test_type_pickle(): # can't actually unpickle, but we can pickle (if in namespace) diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index ac4bd42d3..94f6ef7ad 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -1194,6 +1194,17 @@ class TestCreation: expected = expected * (arr.nbytes // len(expected)) assert arr.tobytes() == expected + @pytest.mark.parametrize("func", [ + np.array, np.asarray, np.asanyarray, np.ascontiguousarray, + np.asfortranarray]) + def test_creation_from_dtypemeta(self, func): + dtype = np.dtype('i') + arr1 = func([1, 2, 3], dtype=dtype) + arr2 = func([1, 2, 3], dtype=type(dtype)) + assert_array_equal(arr1, arr2) + assert arr2.dtype == dtype + + class TestStructured: def test_subarray_field_access(self): a = np.zeros((3, 5), dtype=[('a', ('i4', (2, 2)))]) |