summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/reference/ufuncs.rst9
-rw-r--r--numpy/core/src/multiarray/ctors.c66
-rw-r--r--numpy/core/src/multiarray/item_selection.c286
-rw-r--r--numpy/core/src/npysort/heapsort.c.src44
-rw-r--r--numpy/core/src/npysort/mergesort.c.src78
-rw-r--r--numpy/core/src/npysort/npysort_common.h7
-rw-r--r--numpy/core/src/npysort/quicksort.c.src89
-rw-r--r--numpy/core/src/npysort/selection.c.src48
-rw-r--r--numpy/core/src/private/npy_partition.h.src16
-rw-r--r--numpy/core/src/private/npy_sort.h7
-rw-r--r--numpy/core/src/private/ufunc_override.h9
-rw-r--r--numpy/core/src/umath/ufunc_object.c39
-rw-r--r--numpy/core/src/umath/umathmodule.c7
-rw-r--r--numpy/core/tests/test_multiarray.py17
-rw-r--r--numpy/core/tests/test_regression.py8
-rw-r--r--numpy/core/tests/test_ufunc.py26
-rw-r--r--numpy/lib/shape_base.py7
-rw-r--r--numpy/lib/stride_tricks.py6
-rw-r--r--numpy/lib/tests/test_shape_base.py6
-rw-r--r--numpy/lib/tests/test_stride_tricks.py23
20 files changed, 399 insertions, 399 deletions
diff --git a/doc/source/reference/ufuncs.rst b/doc/source/reference/ufuncs.rst
index f7142bc47..a97534612 100644
--- a/doc/source/reference/ufuncs.rst
+++ b/doc/source/reference/ufuncs.rst
@@ -350,7 +350,8 @@ advanced usage and will not typically be used.
.. versionadded:: 1.6
- Overrides the dtype of the calculation and output arrays. Similar to *sig*.
+ Overrides the dtype of the calculation and output arrays. Similar to
+ *signature*.
*subok*
@@ -359,7 +360,7 @@ advanced usage and will not typically be used.
Defaults to true. If set to false, the output will always be a strict
array, not a subtype.
-*sig*
+*signature*
Either a data-type, a tuple of data-types, or a special signature
string indicating the input and output types of a ufunc. This argument
@@ -370,7 +371,9 @@ advanced usage and will not typically be used.
available and searching for a loop with data-types to which all inputs
can be cast safely. This keyword argument lets you bypass that
search and choose a particular loop. A list of available signatures is
- provided by the **types** attribute of the ufunc object.
+ provided by the **types** attribute of the ufunc object. For backwards
+ compatibility this argument can also be provided as *sig*, although
+ the long form is preferred.
*extobj*
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index 010420826..e88e17028 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -2069,6 +2069,51 @@ PyArray_FromStructInterface(PyObject *input)
return NULL;
}
+/*
+ * Checks if the object in descr is the default 'descr' member for the
+ * __array_interface__ dictionary with 'typestr' member typestr.
+ */
+NPY_NO_EXPORT int
+_is_default_descr(PyObject *descr, PyObject *typestr) {
+ PyObject *tuple, *name, *typestr2;
+#if defined(NPY_PY3K)
+ PyObject *tmp = NULL;
+#endif
+ int ret = 0;
+
+ if (!PyList_Check(descr) || PyList_GET_SIZE(descr) != 1) {
+ return 0;
+ }
+ tuple = PyList_GET_ITEM(descr, 0);
+ if (!(PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2)) {
+ return 0;
+ }
+ name = PyTuple_GET_ITEM(tuple, 0);
+ if (!(PyUString_Check(name) && PyUString_GET_SIZE(name) == 0)) {
+ return 0;
+ }
+ typestr2 = PyTuple_GET_ITEM(tuple, 1);
+#if defined(NPY_PY3K)
+ /* Allow unicode type strings */
+ if (PyUnicode_Check(typestr2)) {
+ tmp = PyUnicode_AsASCIIString(typestr2);
+ if (tmp == NULL) {
+ return 0;
+ }
+ typestr2 = tmp;
+ }
+#endif
+ if (PyBytes_Check(typestr2) &&
+ PyObject_RichCompareBool(typestr, typestr2, Py_EQ)) {
+ ret = 1;
+ }
+#if defined(NPY_PY3K)
+ Py_XDECREF(tmp);
+#endif
+
+ return ret;
+}
+
#define PyIntOrLong_Check(obj) (PyInt_Check(obj) || PyLong_Check(obj))
/*NUMPY_API*/
@@ -2087,11 +2132,6 @@ PyArray_FromInterface(PyObject *origin)
npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS];
int dataflags = NPY_ARRAY_BEHAVED;
- /* Get the typestring -- ignore array_descr */
- /* Get the shape */
- /* Get the memory from __array_data__ and __array_offset__ */
- /* Get the strides */
-
iface = PyArray_GetAttrString_SuppressException(origin,
"__array_interface__");
if (iface == NULL) {
@@ -2135,6 +2175,22 @@ PyArray_FromInterface(PyObject *origin)
goto fail;
}
+ /*
+ * If the dtype is NPY_VOID, see if there is extra information in
+ * the 'descr' attribute.
+ */
+ if (dtype->type_num == NPY_VOID) {
+ PyObject *descr = PyDict_GetItemString(iface, "descr");
+ PyArray_Descr *new_dtype = NULL;
+
+ if (descr != NULL && !_is_default_descr(descr, attr) &&
+ PyArray_DescrConverter2(descr, &new_dtype) == NPY_SUCCEED &&
+ new_dtype != NULL) {
+ Py_DECREF(dtype);
+ dtype = new_dtype;
+ }
+ }
+
/* Get shape tuple from interface specification */
attr = PyDict_GetItemString(iface, "shape");
if (attr == NULL) {
diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c
index e2ef8efdd..0d9474be3 100644
--- a/numpy/core/src/multiarray/item_selection.c
+++ b/numpy/core/src/multiarray/item_selection.c
@@ -1093,87 +1093,20 @@ fail:
}
-/* Be sure to save this global_compare when necessary */
-static PyArrayObject *global_obj;
-
-static int
-sortCompare (const void *a, const void *b)
-{
- return PyArray_DESCR(global_obj)->f->compare(a,b,global_obj);
-}
-
-/*
- * Consumes reference to ap (op gets it) op contains a version of
- * the array with axes swapped if local variable axis is not the
- * last dimension. Origin must be defined locally.
- */
-#define SWAPAXES(op, ap) { \
- orign = PyArray_NDIM(ap)-1; \
- if (axis != orign) { \
- (op) = (PyArrayObject *)PyArray_SwapAxes((ap), axis, orign); \
- Py_DECREF((ap)); \
- if ((op) == NULL) return NULL; \
- } \
- else (op) = (ap); \
- }
-
-/*
- * Consumes reference to ap (op gets it) origin must be previously
- * defined locally. SWAPAXES must have been called previously.
- * op contains the swapped version of the array.
- */
-#define SWAPBACK(op, ap) { \
- if (axis != orign) { \
- (op) = (PyArrayObject *)PyArray_SwapAxes((ap), axis, orign); \
- Py_DECREF((ap)); \
- if ((op) == NULL) return NULL; \
- } \
- else (op) = (ap); \
- }
-
-/* These swap axes in-place if necessary */
-#define SWAPINTP(a,b) {npy_intp c; c=(a); (a) = (b); (b) = c;}
-#define SWAPAXES2(ap) { \
- orign = PyArray_NDIM(ap)-1; \
- if (axis != orign) { \
- SWAPINTP(PyArray_DIMS(ap)[axis], PyArray_DIMS(ap)[orign]); \
- SWAPINTP(PyArray_STRIDES(ap)[axis], PyArray_STRIDES(ap)[orign]); \
- PyArray_UpdateFlags(ap, NPY_ARRAY_C_CONTIGUOUS | \
- NPY_ARRAY_F_CONTIGUOUS); \
- } \
- }
-
-#define SWAPBACK2(ap) { \
- if (axis != orign) { \
- SWAPINTP(PyArray_DIMS(ap)[axis], PyArray_DIMS(ap)[orign]); \
- SWAPINTP(PyArray_STRIDES(ap)[axis], PyArray_STRIDES(ap)[orign]); \
- PyArray_UpdateFlags(ap, NPY_ARRAY_C_CONTIGUOUS | \
- NPY_ARRAY_F_CONTIGUOUS); \
- } \
- }
-
/*NUMPY_API
* Sort an array in-place
*/
NPY_NO_EXPORT int
PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which)
{
- PyArrayObject *ap = NULL, *store_arr = NULL;
- char *ip;
- npy_intp i, n, m;
- int elsize, orign;
- int res = 0;
+ PyArray_SortFunc *sort;
int axis_orig = axis;
- int (*sort)(void *, size_t, size_t, npy_comparator);
+ int n = PyArray_NDIM(op);
- n = PyArray_NDIM(op);
- if ((n == 0) || (PyArray_SIZE(op) == 1)) {
- return 0;
- }
if (axis < 0) {
axis += n;
}
- if ((axis < 0) || (axis >= n)) {
+ if (axis < 0 || axis >= n) {
PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis_orig);
return -1;
}
@@ -1181,83 +1114,35 @@ PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which)
return -1;
}
- /* Determine if we should use type-specific algorithm or not */
- if (PyArray_DESCR(op)->f->sort[which] != NULL) {
- return _new_sortlike(op, axis, PyArray_DESCR(op)->f->sort[which],
- NULL, NULL, 0);
- }
-
- if (PyArray_DESCR(op)->f->compare == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "type does not have compare function");
+ if (which < 0 || which >= NPY_NSORTS) {
+ PyErr_SetString(PyExc_ValueError, "not a valid sort kind");
return -1;
}
- SWAPAXES2(op);
-
- switch (which) {
- case NPY_QUICKSORT :
- sort = npy_quicksort;
- break;
- case NPY_HEAPSORT :
- sort = npy_heapsort;
- break;
- case NPY_MERGESORT :
- sort = npy_mergesort;
- break;
- default:
+ sort = PyArray_DESCR(op)->f->sort[which];
+ if (sort == NULL) {
+ if (PyArray_DESCR(op)->f->compare) {
+ switch (which) {
+ default:
+ case NPY_QUICKSORT:
+ sort = npy_quicksort;
+ break;
+ case NPY_HEAPSORT:
+ sort = npy_heapsort;
+ break;
+ case NPY_MERGESORT:
+ sort = npy_mergesort;
+ break;
+ }
+ }
+ else {
PyErr_SetString(PyExc_TypeError,
- "requested sort kind is not supported");
- goto fail;
- }
-
- ap = (PyArrayObject *)PyArray_FromAny((PyObject *)op,
- NULL, 1, 0,
- NPY_ARRAY_DEFAULT | NPY_ARRAY_UPDATEIFCOPY, NULL);
- if (ap == NULL) {
- goto fail;
- }
- elsize = PyArray_DESCR(ap)->elsize;
- m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1];
- if (m == 0) {
- goto finish;
- }
- n = PyArray_SIZE(ap)/m;
-
- /* Store global -- allows re-entry -- restore before leaving*/
- store_arr = global_obj;
- global_obj = ap;
- for (ip = PyArray_DATA(ap), i = 0; i < n; i++, ip += elsize*m) {
- res = sort(ip, m, elsize, sortCompare);
- if (res < 0) {
- break;
+ "type does not have compare function");
+ return -1;
}
}
- global_obj = store_arr;
- if (PyErr_Occurred()) {
- goto fail;
- }
- else if (res == -NPY_ENOMEM) {
- PyErr_NoMemory();
- goto fail;
- }
- else if (res == -NPY_ECOMP) {
- PyErr_SetString(PyExc_TypeError,
- "sort comparison failed");
- goto fail;
- }
-
-
- finish:
- Py_DECREF(ap); /* Should update op if needed */
- SWAPBACK2(op);
- return 0;
-
- fail:
- Py_XDECREF(ap);
- SWAPBACK2(op);
- return -1;
+ return _new_sortlike(op, axis, sort, NULL, NULL, 0);
}
@@ -1297,7 +1182,8 @@ partition_prep_kth_array(PyArrayObject * ktharray,
if (kth[i] < 0) {
kth[i] += shape[axis];
}
- if (PyArray_SIZE(op) != 0 && ((kth[i] < 0) || (kth[i] >= shape[axis]))) {
+ if (PyArray_SIZE(op) != 0 &&
+ (kth[i] < 0 || kth[i] >= shape[axis])) {
PyErr_Format(PyExc_ValueError, "kth(=%zd) out of bounds (%zd)",
kth[i], shape[axis]);
Py_XDECREF(kthrvl);
@@ -1309,124 +1195,68 @@ partition_prep_kth_array(PyArrayObject * ktharray,
* sort the array of kths so the partitions will
* not trample on each other
*/
- PyArray_Sort(kthrvl, -1, NPY_QUICKSORT);
+ if (PyArray_SIZE(kthrvl) > 1) {
+ PyArray_Sort(kthrvl, -1, NPY_QUICKSORT);
+ }
return kthrvl;
}
-
/*NUMPY_API
* Partition an array in-place
*/
NPY_NO_EXPORT int
-PyArray_Partition(PyArrayObject *op, PyArrayObject * ktharray, int axis, NPY_SELECTKIND which)
+PyArray_Partition(PyArrayObject *op, PyArrayObject * ktharray, int axis,
+ NPY_SELECTKIND which)
{
- PyArrayObject *ap = NULL, *store_arr = NULL;
- char *ip;
- npy_intp i, n, m;
- int elsize, orign;
- int res = 0;
+ PyArrayObject *kthrvl;
+ PyArray_PartitionFunc *part;
+ PyArray_SortFunc *sort;
int axis_orig = axis;
- int (*sort)(void *, size_t, size_t, npy_comparator);
- PyArray_PartitionFunc * part = get_partition_func(PyArray_TYPE(op), which);
+ int n = PyArray_NDIM(op);
+ int ret;
- n = PyArray_NDIM(op);
- if (n == 0) {
- return 0;
- }
if (axis < 0) {
axis += n;
}
- if ((axis < 0) || (axis >= n)) {
+ if (axis < 0 || axis >= n) {
PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis_orig);
return -1;
}
- if (PyArray_FailUnlessWriteable(op, "sort array") < 0) {
+ if (PyArray_FailUnlessWriteable(op, "partition array") < 0) {
return -1;
}
- if (part) {
- PyArrayObject * kthrvl = partition_prep_kth_array(ktharray, op, axis);
- if (kthrvl == NULL)
- return -1;
-
- res = _new_sortlike(op, axis, NULL,
- part,
- PyArray_DATA(kthrvl),
- PyArray_SIZE(kthrvl));
- Py_DECREF(kthrvl);
- return res;
- }
-
-
- if (PyArray_DESCR(op)->f->compare == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "type does not have compare function");
- return -1;
+ if (which < 0 || which >= NPY_NSELECTS) {
+ PyErr_SetString(PyExc_ValueError, "not a valid partition kind");
+ return NULL;
}
-
- SWAPAXES2(op);
-
- /* select not implemented, use quicksort, slower but equivalent */
- switch (which) {
- case NPY_INTROSELECT :
+ part = get_partition_func(PyArray_TYPE(op), which);
+ if (part == NULL) {
+ /* Use sorting, slower but equivalent */
+ if (PyArray_DESCR(op)->f->compare) {
sort = npy_quicksort;
- break;
- default:
+ }
+ else {
PyErr_SetString(PyExc_TypeError,
- "requested sort kind is not supported");
- goto fail;
- }
-
- ap = (PyArrayObject *)PyArray_FromAny((PyObject *)op,
- NULL, 1, 0,
- NPY_ARRAY_DEFAULT | NPY_ARRAY_UPDATEIFCOPY, NULL);
- if (ap == NULL) {
- goto fail;
- }
- elsize = PyArray_DESCR(ap)->elsize;
- m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1];
- if (m == 0) {
- goto finish;
- }
- n = PyArray_SIZE(ap)/m;
-
- /* Store global -- allows re-entry -- restore before leaving*/
- store_arr = global_obj;
- global_obj = ap;
- /* we don't need to care about kth here as we are using a full sort */
- for (ip = PyArray_DATA(ap), i = 0; i < n; i++, ip += elsize*m) {
- res = sort(ip, m, elsize, sortCompare);
- if (res < 0) {
- break;
+ "type does not have compare function");
+ return NULL;
}
}
- global_obj = store_arr;
- if (PyErr_Occurred()) {
- goto fail;
- }
- else if (res == -NPY_ENOMEM) {
- PyErr_NoMemory();
- goto fail;
- }
- else if (res == -NPY_ECOMP) {
- PyErr_SetString(PyExc_TypeError,
- "sort comparison failed");
- goto fail;
+ /* Process ktharray even if using sorting to do bounds checking */
+ kthrvl = partition_prep_kth_array(ktharray, op, axis);
+ if (kthrvl == NULL) {
+ return -1;
}
+ ret = _new_sortlike(op, axis, sort, part,
+ PyArray_DATA(kthrvl), PyArray_SIZE(kthrvl));
- finish:
- Py_DECREF(ap); /* Should update op if needed */
- SWAPBACK2(op);
- return 0;
+ Py_DECREF(kthrvl);
- fail:
- Py_XDECREF(ap);
- SWAPBACK2(op);
- return -1;
+ return ret;
}
diff --git a/numpy/core/src/npysort/heapsort.c.src b/numpy/core/src/npysort/heapsort.c.src
index bec8cc032..88f7978cc 100644
--- a/numpy/core/src/npysort/heapsort.c.src
+++ b/numpy/core/src/npysort/heapsort.c.src
@@ -287,30 +287,27 @@ aheapsort_@suff@(@type@ *v, npy_intp *tosort, npy_intp n, PyArrayObject *arr)
*/
-/*
- * This sort has almost the same signature as libc qsort and is intended to
- * provide a heapsort for array types that don't have type specific sorts.
- * The difference in the signature is an error return, as it might be the
- * case that a memory allocation fails.
- */
int
-npy_heapsort(void *base, size_t num, size_t size, npy_comparator cmp)
+npy_heapsort(char *start, npy_intp num, PyArrayObject *arr)
{
- char *tmp = malloc(size);
- char *a = (char *) base - size;
- size_t i, j, l;
+ npy_intp elsize = PyArray_ITEMSIZE(arr);
+ PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
+ char *tmp = malloc(elsize);
+ char *a = start - elsize;
+ npy_intp i, j, l;
if (tmp == NULL) {
return -NPY_ENOMEM;
}
for (l = num >> 1; l > 0; --l) {
- GENERIC_COPY(tmp, a + l*size, size);
+ GENERIC_COPY(tmp, a + l*elsize, elsize);
for (i = l, j = l << 1; j <= num;) {
- if (j < num && GENERIC_LT(a + j*size, a + (j+1)*size, cmp))
- j += 1;
- if (GENERIC_LT(tmp, a + j*size, cmp)) {
- GENERIC_COPY(a + i*size, a + j*size, size);
+ if (j < num && cmp(a + j*elsize, a + (j+1)*elsize, arr) < 0) {
+ ++j;
+ }
+ if (cmp(tmp, a + j*elsize, arr) < 0) {
+ GENERIC_COPY(a + i*elsize, a + j*elsize, elsize);
i = j;
j += j;
}
@@ -318,18 +315,19 @@ npy_heapsort(void *base, size_t num, size_t size, npy_comparator cmp)
break;
}
}
- GENERIC_COPY(a + i*size, tmp, size);
+ GENERIC_COPY(a + i*elsize, tmp, elsize);
}
for (; num > 1;) {
- GENERIC_COPY(tmp, a + num*size, size);
- GENERIC_COPY(a + num*size, a + size, size);
+ GENERIC_COPY(tmp, a + num*elsize, elsize);
+ GENERIC_COPY(a + num*elsize, a + elsize, elsize);
num -= 1;
for (i = 1, j = 2; j <= num;) {
- if (j < num && GENERIC_LT(a + j*size, a + (j+1)*size, cmp))
- j++;
- if (GENERIC_LT(tmp, a + j*size, cmp)) {
- GENERIC_COPY(a + i*size, a + j*size, size);
+ if (j < num && cmp(a + j*elsize, a + (j+1)*elsize, arr) < 0) {
+ ++j;
+ }
+ if (cmp(tmp, a + j*elsize, arr) < 0) {
+ GENERIC_COPY(a + i*elsize, a + j*elsize, elsize);
i = j;
j += j;
}
@@ -337,7 +335,7 @@ npy_heapsort(void *base, size_t num, size_t size, npy_comparator cmp)
break;
}
}
- GENERIC_COPY(a + i*size, tmp, size);
+ GENERIC_COPY(a + i*elsize, tmp, elsize);
}
free(tmp);
diff --git a/numpy/core/src/npysort/mergesort.c.src b/numpy/core/src/npysort/mergesort.c.src
index ecf39720a..406be66d0 100644
--- a/numpy/core/src/npysort/mergesort.c.src
+++ b/numpy/core/src/npysort/mergesort.c.src
@@ -350,80 +350,70 @@ amergesort_@suff@(@type@ *v, npy_intp *tosort, npy_intp num, PyArrayObject *arr)
static void
-npy_mergesort0(char *pl, char *pr, char *pw, char *vp, size_t size, npy_comparator cmp)
+npy_mergesort0(char *pl, char *pr, char *pw, char *vp, npy_intp elsize,
+ PyArray_CompareFunc *cmp, PyArrayObject *arr)
{
char *pi, *pj, *pk, *pm;
- if ((size_t)(pr - pl) > SMALL_MERGESORT*size) {
+ if (pr - pl > SMALL_MERGESORT*elsize) {
/* merge sort */
- pm = pl + (((pr - pl)/size) >> 1)*size;
- npy_mergesort0(pl, pm, pw, vp, size, cmp);
- npy_mergesort0(pm, pr, pw, vp, size, cmp);
+ pm = pl + (((pr - pl)/elsize) >> 1)*elsize;
+ npy_mergesort0(pl, pm, pw, vp, elsize, cmp, arr);
+ npy_mergesort0(pm, pr, pw, vp, elsize, cmp, arr);
GENERIC_COPY(pw, pl, pm - pl);
pi = pw + (pm - pl);
pj = pw;
pk = pl;
while (pj < pi && pm < pr) {
- if (GENERIC_LT(pm, pj, cmp)) {
- GENERIC_COPY(pk, pm, size);
- pm += size;
- pk += size;
+ if (cmp(pm, pj, arr) < 0) {
+ GENERIC_COPY(pk, pm, elsize);
+ pm += elsize;
+ pk += elsize;
}
else {
- GENERIC_COPY(pk, pj, size);
- pj += size;
- pk += size;
+ GENERIC_COPY(pk, pj, elsize);
+ pj += elsize;
+ pk += elsize;
}
}
GENERIC_COPY(pk, pj, pi - pj);
}
else {
/* insertion sort */
- for (pi = pl + size; pi < pr; pi += size) {
- GENERIC_COPY(vp, pi, size);
+ for (pi = pl + elsize; pi < pr; pi += elsize) {
+ GENERIC_COPY(vp, pi, elsize);
pj = pi;
- pk = pi - size;
- while (pj > pl && GENERIC_LT(vp, pk, cmp)) {
- GENERIC_COPY(pj, pk, size);
- pj -= size;
- pk -= size;
+ pk = pi - elsize;
+ while (pj > pl && cmp(vp, pk, arr) < 0) {
+ GENERIC_COPY(pj, pk, elsize);
+ pj -= elsize;
+ pk -= elsize;
}
- GENERIC_COPY(pj, vp, size);
+ GENERIC_COPY(pj, vp, elsize);
}
}
}
-/*
- * This sort has almost the same signature as libc qsort and is intended to
- * provide a mergesort for array types that don't have type specific sorts.
- * The difference in the signature is an error return, as it might be the
- * case that a memory allocation fails.
- */
int
-npy_mergesort(void *base, size_t num, size_t size, npy_comparator cmp)
+npy_mergesort(char *start, npy_intp num, PyArrayObject *arr)
{
- char *pl, *pr, *pw, *vp;
- int err = 0;
-
- pl = base;
- pr = pl + num*size;
- pw = malloc((num/2) * size);
- if (pw == NULL) {
- err = -NPY_ENOMEM;
- goto fail_0;
- }
- vp = malloc(size);
- if (vp == NULL) {
- err = -NPY_ENOMEM;
- goto fail_1;
+ npy_intp elsize = PyArray_ITEMSIZE(arr);
+ PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
+ char *pl = start;
+ char *pr = pl + num*elsize;
+ char *pw = malloc((num >> 1) *elsize);
+ char *vp = malloc(elsize);
+ int err = -NPY_ENOMEM;
+
+ if (pw != NULL && vp != NULL) {
+ npy_mergesort0(pl, pr, pw, vp, elsize, cmp, arr);
+ err = 0;
}
- npy_mergesort0(pl, pr, pw, vp, size, cmp);
free(vp);
-fail_1:
free(pw);
-fail_0:
+
return err;
}
diff --git a/numpy/core/src/npysort/npysort_common.h b/numpy/core/src/npysort/npysort_common.h
index a3ce7664d..a22045b41 100644
--- a/numpy/core/src/npysort/npysort_common.h
+++ b/numpy/core/src/npysort/npysort_common.h
@@ -357,11 +357,4 @@ GENERIC_SWAP(char *a, char *b, size_t len)
}
}
-
-NPY_INLINE static int
-GENERIC_LT(char *a, char *b, int (*cmp)(const void *, const void *))
-{
- return cmp(a, b) < 0;
-}
-
#endif
diff --git a/numpy/core/src/npysort/quicksort.c.src b/numpy/core/src/npysort/quicksort.c.src
index fcde70957..5334aca76 100644
--- a/numpy/core/src/npysort/quicksort.c.src
+++ b/numpy/core/src/npysort/quicksort.c.src
@@ -216,6 +216,10 @@ quicksort_@suff@(@type@ *start, npy_intp num, PyArrayObject *arr)
@type@ *pr = start + (num - 1)*len;
@type@ *stack[PYA_QS_STACK], **sptr = stack, *pm, *pi, *pj, *pk;
+ if (vp == NULL) {
+ return -NPY_ENOMEM;
+ }
+
for (;;) {
while ((size_t)(pr - pl) > SMALL_QUICKSORT*len) {
/* quicksort partition */
@@ -350,15 +354,86 @@ aquicksort_@suff@(@type@ *v, npy_intp* tosort, npy_intp num, PyArrayObject *arr)
*/
-/*
- * This sort has almost the same signature as libc qsort and is intended to
- * supply an error return for compatibility with the other generic sort
- * kinds.
- */
int
-npy_quicksort(void *base, size_t num, size_t size, npy_comparator cmp)
+npy_quicksort(char *start, npy_intp num, PyArrayObject *arr)
{
- qsort(base, num, size, cmp);
+ npy_intp elsize = PyArray_ITEMSIZE(arr);
+ PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
+ char *vp = malloc(elsize);
+ char *pl = start;
+ char *pr = start + (num - 1)*elsize;
+ char *stack[PYA_QS_STACK];
+ char **sptr = stack;
+ char *pm, *pi, *pj, *pk;
+
+ if (vp == NULL) {
+ return -NPY_ENOMEM;
+ }
+
+ for (;;) {
+ while(pr - pl > SMALL_QUICKSORT*elsize) {
+ /* quicksort partition */
+ pm = pl + (((pr - pl) / elsize) >> 1) * elsize;
+ if (cmp(pm, pl, arr) < 0) {
+ GENERIC_SWAP(pm, pl, elsize);
+ }
+ if (cmp(pr, pm, arr) < 0) {
+ GENERIC_SWAP(pr, pm, elsize);
+ }
+ if (cmp(pm, pl, arr) < 0) {
+ GENERIC_SWAP(pm, pl, elsize);
+ }
+ GENERIC_COPY(vp, pm, elsize);
+ pi = pl;
+ pj = pr - elsize;
+ GENERIC_SWAP(pm, pj, elsize);
+ for (;;) {
+ do {
+ pi += elsize;
+ } while (cmp(pi, vp, arr) < 0);
+ do {
+ pj -= elsize;
+ } while (cmp(vp, pj, arr) < 0);
+ if (pi >= pj) {
+ break;
+ }
+ GENERIC_SWAP(pi, pj, elsize);
+ }
+ pk = pr - elsize;
+ GENERIC_SWAP(pi, pk, elsize);
+ /* push largest partition on stack */
+ if (pi - pl < pr - pi) {
+ *sptr++ = pi + elsize;
+ *sptr++ = pr;
+ pr = pi - elsize;
+ }
+ else {
+ *sptr++ = pl;
+ *sptr++ = pi - elsize;
+ pl = pi + elsize;
+ }
+ }
+
+ /* insertion sort */
+ for (pi = pl + elsize; pi <= pr; pi += elsize) {
+ GENERIC_COPY(vp, pi, elsize);
+ pj = pi;
+ pk = pi - elsize;
+ while (pj > pl && cmp(vp, pk, arr) < 0) {
+ GENERIC_COPY(pj, pk, elsize);
+ pj -= elsize;
+ pk -= elsize;
+ }
+ GENERIC_COPY(pj, vp, elsize);
+ }
+ if (sptr == stack) {
+ break;
+ }
+ pr = *(--sptr);
+ pl = *(--sptr);
+ }
+
+ free(vp);
return 0;
}
diff --git a/numpy/core/src/npysort/selection.c.src b/numpy/core/src/npysort/selection.c.src
index 4167b2694..51c7d6721 100644
--- a/numpy/core/src/npysort/selection.c.src
+++ b/numpy/core/src/npysort/selection.c.src
@@ -424,51 +424,3 @@ int
/**end repeat1**/
/**end repeat**/
-
-
-/*
- *****************************************************************************
- ** STRING SORTS **
- *****************************************************************************
- */
-
-
-/**begin repeat
- *
- * #TYPE = STRING, UNICODE#
- * #suff = string, unicode#
- * #type = npy_char, npy_ucs4#
- */
-
-int
-introselect_@suff@(@type@ *start, npy_intp num, npy_intp kth, PyArrayObject *arr)
-{
- return quicksort_@suff@(start, num, arr);
-}
-
-int
-aintroselect_@suff@(@type@ *v, npy_intp* tosort, npy_intp num, npy_intp kth, void *null)
-{
- return aquicksort_@suff@(v, tosort, num, null);
-}
-
-/**end repeat**/
-
-
-/*
- *****************************************************************************
- ** GENERIC SORT **
- *****************************************************************************
- */
-
-
-/*
- * This sort has almost the same signature as libc qsort and is intended to
- * supply an error return for compatibility with the other generic sort
- * kinds.
- */
-int
-npy_introselect(void *base, size_t num, size_t size, size_t kth, npy_comparator cmp)
-{
- return npy_quicksort(base, num, size, cmp);
-}
diff --git a/numpy/core/src/private/npy_partition.h.src b/numpy/core/src/private/npy_partition.h.src
index fd79068f7..07aecd4f8 100644
--- a/numpy/core/src/private/npy_partition.h.src
+++ b/numpy/core/src/private/npy_partition.h.src
@@ -55,22 +55,6 @@ NPY_VISIBILITY_HIDDEN int aintroselect_@suff@(@type@ *v, npy_intp* tosort, npy_i
/**end repeat**/
-NPY_VISIBILITY_HIDDEN int introselect_string(npy_char *vec, npy_intp cnt,
- npy_intp kth, PyArrayObject *arr);
-NPY_VISIBILITY_HIDDEN int aintroselect_string(npy_char *vec, npy_intp *ind,
- npy_intp cnt, npy_intp kth,
- void *null);
-
-
-NPY_VISIBILITY_HIDDEN int introselect_unicode(npy_ucs4 *vec, npy_intp cnt,
- npy_intp kth, PyArrayObject *arr);
-NPY_VISIBILITY_HIDDEN int aintroselect_unicode(npy_ucs4 *vec, npy_intp *ind,
- npy_intp cnt, npy_intp kth,
- void *null);
-
-NPY_VISIBILITY_HIDDEN int npy_introselect(void *base, size_t num, size_t size,
- size_t kth, npy_comparator cmp);
-
typedef struct {
enum NPY_TYPES typenum;
PyArray_PartitionFunc * part[NPY_NSELECTS];
diff --git a/numpy/core/src/private/npy_sort.h b/numpy/core/src/private/npy_sort.h
index 7c5701f88..85630b2df 100644
--- a/numpy/core/src/private/npy_sort.h
+++ b/numpy/core/src/private/npy_sort.h
@@ -9,7 +9,6 @@
#define NPY_ENOMEM 1
#define NPY_ECOMP 2
-typedef int (*npy_comparator)(const void *, const void *);
int quicksort_bool(npy_bool *vec, npy_intp cnt, void *null);
int heapsort_bool(npy_bool *vec, npy_intp cnt, void *null);
@@ -187,9 +186,9 @@ int aheapsort_timedelta(npy_timedelta *vec, npy_intp *ind, npy_intp cnt, void *n
int amergesort_timedelta(npy_timedelta *vec, npy_intp *ind, npy_intp cnt, void *null);
-int npy_quicksort(void *base, size_t num, size_t size, npy_comparator cmp);
-int npy_heapsort(void *base, size_t num, size_t size, npy_comparator cmp);
-int npy_mergesort(void *base, size_t num, size_t size, npy_comparator cmp);
+int npy_quicksort(char *vec, npy_intp cnt, PyArrayObject *arr);
+int npy_heapsort(char *vec, npy_intp cnt, PyArrayObject *arr);
+int npy_mergesort(char *vec, npy_intp cnt, PyArrayObject *arr);
int npy_aquicksort(char *vec, npy_intp *ind, npy_intp cnt, PyArrayObject *arr);
int npy_aheapsort(char *vec, npy_intp *ind, npy_intp cnt, PyArrayObject *arr);
int npy_amergesort(char *vec, npy_intp *ind, npy_intp cnt, PyArrayObject *arr);
diff --git a/numpy/core/src/private/ufunc_override.h b/numpy/core/src/private/ufunc_override.h
index c47c46a66..c3f9f601e 100644
--- a/numpy/core/src/private/ufunc_override.h
+++ b/numpy/core/src/private/ufunc_override.h
@@ -13,7 +13,14 @@ normalize___call___args(PyUFuncObject *ufunc, PyObject *args,
{
/* ufunc.__call__(*args, **kwds) */
int nargs = PyTuple_GET_SIZE(args);
- PyObject *obj;
+ PyObject *obj = PyDict_GetItemString(*normal_kwds, "sig");
+
+ /* ufuncs accept 'sig' or 'signature' normalize to 'signature' */
+ if (obj != NULL) {
+ Py_INCREF(obj);
+ PyDict_SetItemString(*normal_kwds, "signature", obj);
+ PyDict_DelItemString(*normal_kwds, "sig");
+ }
*normal_args = PyTuple_GetSlice(args, 0, nin);
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 5e4a49553..36201b3ea 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -808,6 +808,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
int type_num;
int any_flexible = 0, any_object = 0, any_flexible_userloops = 0;
+ int has_sig = 0;
ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>";
@@ -945,7 +946,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
switch (str[0]) {
case 'c':
/* Provides a policy for allowed casting */
- if (strncmp(str, "casting", 7) == 0) {
+ if (strcmp(str, "casting") == 0) {
if (!PyArray_CastingConverter(value, out_casting)) {
goto fail;
}
@@ -954,7 +955,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
break;
case 'd':
/* Another way to specify 'sig' */
- if (strncmp(str, "dtype", 5) == 0) {
+ if (strcmp(str, "dtype") == 0) {
/* Allow this parameter to be None */
PyArray_Descr *dtype;
if (!PyArray_DescrConverter2(value, &dtype)) {
@@ -976,7 +977,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
* Overrides the global parameters buffer size,
* error mask, and error object
*/
- if (strncmp(str, "extobj", 6) == 0) {
+ if (strcmp(str, "extobj") == 0) {
*out_extobj = value;
bad_arg = 0;
}
@@ -987,7 +988,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
* either as a single array or None for single output
* ufuncs, or as a tuple of arrays and Nones.
*/
- if (strncmp(str, "out", 3) == 0) {
+ if (strcmp(str, "out") == 0) {
if (nargs > nin) {
PyErr_SetString(PyExc_ValueError,
"cannot specify 'out' as both a "
@@ -1048,7 +1049,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
bad_arg = 0;
}
/* Allows the default output layout to be overridden */
- else if (strncmp(str,"order",5) == 0) {
+ else if (strcmp(str, "order") == 0) {
if (!PyArray_OrderConverter(value, out_order)) {
goto fail;
}
@@ -1057,7 +1058,13 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
break;
case 's':
/* Allows a specific function inner loop to be selected */
- if (strncmp(str,"sig",3) == 0) {
+ if (strcmp(str, "sig") == 0 ||
+ strcmp(str, "signature") == 0) {
+ if (has_sig == 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot specify both 'sig' and 'signature'");
+ goto fail;
+ }
if (*out_typetup != NULL) {
PyErr_SetString(PyExc_RuntimeError,
"cannot specify both 'sig' and 'dtype'");
@@ -1066,8 +1073,9 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
*out_typetup = value;
Py_INCREF(value);
bad_arg = 0;
+ has_sig = 1;
}
- else if (strncmp(str,"subok",5) == 0) {
+ else if (strcmp(str, "subok") == 0) {
if (!PyBool_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"'subok' must be a boolean");
@@ -1082,8 +1090,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc,
* Provides a boolean array 'where=' mask if
* out_wheremask is supplied.
*/
- if (out_wheremask != NULL &&
- strncmp(str,"where",5) == 0) {
+ if (out_wheremask != NULL && strcmp(str, "where") == 0) {
PyArray_Descr *dtype;
dtype = PyArray_DescrFromType(NPY_BOOL);
if (dtype == NULL) {
@@ -4450,6 +4457,14 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data,
{
PyUFuncObject *ufunc;
+ if (nin + nout > NPY_MAXARGS) {
+ PyErr_Format(PyExc_ValueError,
+ "Cannot construct a ufunc with more than %d operands "
+ "(requested number were: inputs = %d and outputs = %d)",
+ NPY_MAXARGS, nin, nout);
+ return NULL;
+ }
+
ufunc = PyArray_malloc(sizeof(PyUFuncObject));
if (ufunc == NULL) {
return NULL;
@@ -5094,6 +5109,12 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args)
return NULL;
}
+ if (ufunc->nout != 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "Only single output ufuncs supported at this time");
+ return NULL;
+ }
+
if (!PyArg_ParseTuple(args, "OO|O", &op1, &idx, &op2)) {
return NULL;
}
diff --git a/numpy/core/src/umath/umathmodule.c b/numpy/core/src/umath/umathmodule.c
index d792e8b24..624588410 100644
--- a/numpy/core/src/umath/umathmodule.c
+++ b/numpy/core/src/umath/umathmodule.c
@@ -103,6 +103,13 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS
PyErr_SetString(PyExc_TypeError, "function must be callable");
return NULL;
}
+ if (nin + nout > NPY_MAXARGS) {
+ PyErr_Format(PyExc_ValueError,
+ "Cannot construct a ufunc with more than %d operands "
+ "(requested number were: inputs = %d and outputs = %d)",
+ NPY_MAXARGS, nin, nout);
+ return NULL;
+ }
self = PyArray_malloc(sizeof(PyUFuncObject));
if (self == NULL) {
return NULL;
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 0c13cff6a..f6ed6dd84 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -1436,7 +1436,6 @@ class TestMethods(TestCase):
assert_raises(ValueError, d_obj.argpartition, 10)
assert_raises(ValueError, d_obj.argpartition, -11)
- @dec.knownfailureif(True, "Ticket #5469 fixed for argpartition only")
def test_partition_out_of_range(self):
# Test out of range values in kth raise an error, gh-5469
d = np.arange(10)
@@ -2313,6 +2312,22 @@ class TestBinop(object):
assert_equal(obj2.sum(), 42)
assert_(isinstance(obj2, SomeClass2))
+ def test_ufunc_override_normalize_signature(self):
+ # gh-5674
+ class SomeClass(object):
+ def __numpy_ufunc__(self, ufunc, method, i, inputs, **kw):
+ return kw
+
+ a = SomeClass()
+ kw = np.add(a, [1])
+ assert_('sig' not in kw and 'signature' not in kw)
+ kw = np.add(a, [1], sig='ii->i')
+ assert_('sig' not in kw and 'signature' in kw)
+ assert_equal(kw['signature'], 'ii->i')
+ kw = np.add(a, [1], signature='ii->i')
+ assert_('sig' not in kw and 'signature' in kw)
+ assert_equal(kw['signature'], 'ii->i')
+
class TestCAPI(TestCase):
def test_IsPythonScalar(self):
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
index 431f80534..19c8d4457 100644
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -2110,6 +2110,14 @@ class TestRegression(TestCase):
test_string = np.string_('')
assert_equal(pickle.loads(pickle.dumps(test_string)), test_string)
+ def test_frompyfunc_many_args(self):
+ # gh-5672
+
+ def passer(*args):
+ pass
+
+ assert_raises(ValueError, np.frompyfunc, passer, 32, 1)
+
if __name__ == "__main__":
run_module_suite()
diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py
index a285d5334..b3c281e2a 100644
--- a/numpy/core/tests/test_ufunc.py
+++ b/numpy/core/tests/test_ufunc.py
@@ -9,6 +9,29 @@ import numpy.core.operand_flag_tests as opflag_tests
from numpy.compat import asbytes
from numpy.core.test_rational import *
+
+class TestUfuncKwargs(TestCase):
+ def test_kwarg_exact(self):
+ assert_raises(TypeError, np.add, 1, 2, castingx='safe')
+ assert_raises(TypeError, np.add, 1, 2, dtypex=np.int)
+ assert_raises(TypeError, np.add, 1, 2, extobjx=[4096])
+ assert_raises(TypeError, np.add, 1, 2, outx=None)
+ assert_raises(TypeError, np.add, 1, 2, sigx='ii->i')
+ assert_raises(TypeError, np.add, 1, 2, signaturex='ii->i')
+ assert_raises(TypeError, np.add, 1, 2, subokx=False)
+ assert_raises(TypeError, np.add, 1, 2, wherex=[True])
+
+ def test_sig_signature(self):
+ assert_raises(ValueError, np.add, 1, 2, sig='ii->i',
+ signature='ii->i')
+
+ def test_sig_dtype(self):
+ assert_raises(RuntimeError, np.add, 1, 2, sig='ii->i',
+ dtype=np.int)
+ assert_raises(RuntimeError, np.add, 1, 2, signature='ii->i',
+ dtype=np.int)
+
+
class TestUfunc(TestCase):
def test_pickle(self):
import pickle
@@ -1072,6 +1095,9 @@ class TestUfunc(TestCase):
self.assertRaises(TypeError, np.add.at, values, [0, 1], 1)
assert_array_equal(values, np.array(['a', 1], dtype=np.object))
+ # Test multiple output ufuncs raise error, gh-5665
+ assert_raises(ValueError, np.modf.at, np.arange(10), [1])
+
def test_reduce_arguments(self):
f = np.add.reduce
d = np.ones((5,2), dtype=int)
diff --git a/numpy/lib/shape_base.py b/numpy/lib/shape_base.py
index 2d18c5bc8..011434dda 100644
--- a/numpy/lib/shape_base.py
+++ b/numpy/lib/shape_base.py
@@ -850,7 +850,12 @@ def tile(A, reps):
except TypeError:
tup = (reps,)
d = len(tup)
- c = _nx.array(A, copy=False, subok=True, ndmin=d)
+ if all(x == 1 for x in tup) and isinstance(A, _nx.ndarray):
+ # Fixes the problem that the function does not make a copy if A is a
+ # numpy array and the repetitions are 1 in all dimensions
+ return _nx.array(A, copy=True, subok=True, ndmin=d)
+ else:
+ c = _nx.array(A, copy=False, subok=True, ndmin=d)
shape = list(c.shape)
n = max(c.size, 1)
if (d < c.ndim):
diff --git a/numpy/lib/stride_tricks.py b/numpy/lib/stride_tricks.py
index a5f247abf..e7649cb60 100644
--- a/numpy/lib/stride_tricks.py
+++ b/numpy/lib/stride_tricks.py
@@ -46,9 +46,11 @@ def as_strided(x, shape=None, strides=None, subok=False):
if strides is not None:
interface['strides'] = tuple(strides)
array = np.asarray(DummyArray(interface, base=x))
- # Make sure dtype is correct in case of custom dtype
- if array.dtype.kind == 'V':
+
+ if array.dtype.fields is None and x.dtype.fields is not None:
+ # This should only happen if x.dtype is [('', 'Vx')]
array.dtype = x.dtype
+
return _maybe_view_as_subclass(x, array)
diff --git a/numpy/lib/tests/test_shape_base.py b/numpy/lib/tests/test_shape_base.py
index 23f3edfbe..fb9d7f364 100644
--- a/numpy/lib/tests/test_shape_base.py
+++ b/numpy/lib/tests/test_shape_base.py
@@ -324,6 +324,12 @@ class TestTile(TestCase):
assert_equal(tile(b, (2, 2)), [[1, 2, 1, 2], [3, 4, 3, 4],
[1, 2, 1, 2], [3, 4, 3, 4]])
+ def test_tile_one_repetition_on_array_gh4679(self):
+ a = np.arange(5)
+ b = tile(a, 1)
+ b += 2
+ assert_equal(a, np.arange(5))
+
def test_empty(self):
a = np.array([[[]]])
d = tile(a, (3, 2, 5)).shape
diff --git a/numpy/lib/tests/test_stride_tricks.py b/numpy/lib/tests/test_stride_tricks.py
index ef483921c..e079e0bf4 100644
--- a/numpy/lib/tests/test_stride_tricks.py
+++ b/numpy/lib/tests/test_stride_tricks.py
@@ -290,6 +290,29 @@ def test_as_strided():
expected = np.array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]])
assert_array_equal(a_view, expected)
+ # Regression test for gh-5081
+ dt = np.dtype([('num', 'i4'), ('obj', 'O')])
+ a = np.empty((4,), dtype=dt)
+ a['num'] = np.arange(1, 5)
+ a_view = as_strided(a, shape=(3, 4), strides=(0, a.itemsize))
+ expected_num = [[1, 2, 3, 4]] * 3
+ expected_obj = [[None]*4]*3
+ assert_equal(a_view.dtype, dt)
+ assert_array_equal(expected_num, a_view['num'])
+ assert_array_equal(expected_obj, a_view['obj'])
+
+ # Make sure that void types without fields are kept unchanged
+ a = np.empty((4,), dtype='V4')
+ a_view = as_strided(a, shape=(3, 4), strides=(0, a.itemsize))
+ assert_equal(a.dtype, a_view.dtype)
+
+ # Make sure that the only type that could fail is properly handled
+ dt = np.dtype({'names': [''], 'formats': ['V4']})
+ a = np.empty((4,), dtype=dt)
+ a_view = as_strided(a, shape=(3, 4), strides=(0, a.itemsize))
+ assert_equal(a.dtype, a_view.dtype)
+
+
class VerySimpleSubClass(np.ndarray):
def __new__(cls, *args, **kwargs):