diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index 3270bc20d1d9..4fd2298113e7 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -429,7 +429,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, * be treated as objects, and they expect numpy to treat it as an object if * __len__ is not defined. */ - if (maxdims == 0 || !PySequence_Check(obj) || PySequence_Size(obj) < 0) { + if (maxdims == 0 || !PyArray_SequenceOrMappingViewCheck(obj) || PySequence_Size(obj) < 0) { /* clear any PySequence_Size error which corrupts further calls */ PyErr_Clear(); diff --git a/numpy/core/src/multiarray/common.h b/numpy/core/src/multiarray/common.h index 487d530a148b..ede6f64ebfc0 100644 --- a/numpy/core/src/multiarray/common.h +++ b/numpy/core/src/multiarray/common.h @@ -129,6 +129,35 @@ check_and_adjust_index(npy_intp *index, npy_intp max_item, int axis, return 0; } +/* + * Returns 1 if *obj is array-like object, otherwise returns 0 + */ +static NPY_INLINE int +PyArray_SequenceOrMappingViewCheck(PyObject *obj) { + /* Check is *obj is sequence*/ + if (PySequence_Check(obj)) { + return 1; + } + + /* Check is *obj inherits collections.abc.MappingView */ + static PyObject *mappingview = NULL; + + if (mappingview == NULL) { + PyObject *mod = PyImport_ImportModule("collections.abc"); + + if (mod != NULL) { + mappingview = PyObject_GetAttrString(mod, "MappingView"); + Py_DECREF(mod); + } + } + + if (PyObject_IsInstance(obj, mappingview) == 1) { + return 1; + } + + return 0; +} + /* * Returns -1 and sets an exception if *axis is an invalid axis for * an array of dimension ndim, otherwise adjusts it in place to be diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index c38067681fb6..d129fc07fd68 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -859,7 +859,7 @@ astype_anyint(PyObject *obj) { return NULL; } if (dtype_guess == NULL) { - if (PySequence_Check(obj) && PySequence_Size(obj) == 0) { + if (PyArray_SequenceOrMappingViewCheck(obj) && PySequence_Size(obj) == 0) { PyErr_SetString(PyExc_TypeError, EMPTY_SEQUENCE_ERR_MSG); } return NULL; @@ -901,7 +901,7 @@ static int int_sequence_to_arrays(PyObject *seq, { int i; - if (!PySequence_Check(seq) || PySequence_Size(seq) != count) { + if (!PyArray_SequenceOrMappingViewCheck(seq) || PySequence_Size(seq) != count) { PyErr_Format(PyExc_ValueError, "parameter %s must be a sequence of length %d", paramname, count); diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index c5199c015b7c..a9eba381acb4 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -624,7 +624,7 @@ setArrayFromSequence(PyArrayObject *a, PyObject *s, NPY_NO_EXPORT int PyArray_AssignFromSequence(PyArrayObject *self, PyObject *v) { - if (!PySequence_Check(v)) { + if (!PyArray_SequenceOrMappingViewCheck(v)) { PyErr_SetString(PyExc_ValueError, "assignment from non-sequence"); return -1; @@ -752,7 +752,7 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it, } /* obj is not a Sequence */ - if (!PySequence_Check(obj) || + if (!PyArray_SequenceOrMappingViewCheck(obj) || PySequence_Length(obj) < 0) { *maxndim = 0; PyErr_Clear(); @@ -1801,7 +1801,7 @@ PyArray_GetArrayParamsFromObject(PyObject *op, } /* Try to treat op as a list of lists or array-like objects. */ - if (!writeable && PySequence_Check(op)) { + if (!writeable && PyArray_SequenceOrMappingViewCheck(op)) { int check_it, stop_at_string, stop_at_tuple, is_object; int type_num, type; diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 58572f268fcd..d3f9a2946c9c 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -1101,6 +1101,22 @@ def test_jagged_shape_object(self): assert_equal(a.shape, (3,)) assert_equal(a.dtype, object) + def test_mapping_view(self): + # Test correct array creation from dict subtypes + d = {0: 1, 2: 3, 4: 5} + + a = np.array(d.items()) + assert_equal(a.shape, (3,2)) + # dtype inferred differently on Windows/Linux/Mac + assert_equal((a.dtype == np.int32 or a.dtype == np.int64), True) + + a = np.array(d.keys()) + assert_equal(a.shape, (3,)) + assert_equal((a.dtype == np.int32 or a.dtype == np.int64), True) + + a = np.array(d.values()) + assert_equal(a.shape, (3,)) + assert_equal((a.dtype == np.int32 or a.dtype == np.int64), True) class TestStructured(object): def test_subarray_field_access(self):