diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 66 | ||||
-rw-r--r-- | numpy/lib/stride_tricks.py | 6 | ||||
-rw-r--r-- | numpy/lib/tests/test_stride_tricks.py | 23 |
3 files changed, 88 insertions, 7 deletions
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/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_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): |