diff options
author | Charles Harris <charlesr.harris@gmail.com> | 2011-03-09 19:46:53 -0700 |
---|---|---|
committer | Charles Harris <charlesr.harris@gmail.com> | 2011-03-09 19:46:53 -0700 |
commit | b94585c25f81d0d90d25d1ce9b2781f478606734 (patch) | |
tree | 3c59c78a2a09b035969fcb074fc86e3b7ebb32a0 | |
parent | ce9fea2c202f80ab39fa2b4866efd08ad075b9f2 (diff) | |
parent | 4aaa4b7e500514a3bc37b515445592d17dce65b1 (diff) | |
download | numpy-b94585c25f81d0d90d25d1ce9b2781f478606734.tar.gz |
Merge branch 'pandas-fix'
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 52 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 29 |
2 files changed, 72 insertions, 9 deletions
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 36e1cb467..97a6fc915 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -688,8 +688,8 @@ discover_dimensions(PyObject *s, int *maxndim, npy_intp *d, int check_it, PyInstance_Check(s) || #endif PySequence_Length(s) < 0) { - PyErr_Clear(); *maxndim = 0; + PyErr_Clear(); return 0; } @@ -822,22 +822,60 @@ discover_dimensions(PyObject *s, int *maxndim, npy_intp *d, int check_it, npy_intp dtmp[NPY_MAXDIMS]; int j, maxndim_m1 = *maxndim - 1; - e = PySequence_GetItem(s, 0); + if ((e = PySequence_GetItem(s, 0)) == NULL) { + /* + * PySequence_Check detects whether an old type object is a + * sequence by the presence of the __getitem__ attribute, and + * for new type objects that aren't dictionaries by the + * presence of the __len__ attribute as well. In either case it + * is possible to have an object that tests as a sequence but + * doesn't behave as a sequence and consequently, the + * PySequence_GetItem call can fail. When that happens and the + * object looks like a dictionary, we truncate the dimensions + * and set the object creation flag, otherwise we pass the + * error back up the call chain. + */ + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + *maxndim = 0; + *out_is_object = 1; + return 0; + } + else { + return -1; + } + } r = discover_dimensions(e, &maxndim_m1, d + 1, check_it, stop_at_string, stop_at_tuple, out_is_object); Py_DECREF(e); + if (r < 0) { + return r; + } /* For the dimension truncation check below */ *maxndim = maxndim_m1 + 1; - for (i = 1; i < n; ++i) { /* Get the dimensions of the first item */ - e = PySequence_GetItem(s, i); + if ((e = PySequence_GetItem(s, i)) == NULL) { + /* see comment above */ + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + *maxndim = 0; + *out_is_object = 1; + return 0; + } + else { + return -1; + } + } r = discover_dimensions(e, &maxndim_m1, dtmp, check_it, stop_at_string, stop_at_tuple, out_is_object); Py_DECREF(e); + if (r < 0) { + return r; + } /* Reduce max_ndim_m1 to just items which match */ for (j = 0; j < maxndim_m1; ++j) { @@ -1511,13 +1549,9 @@ PyArray_GetArrayParamsFromObject(PyObject *op, stop_at_string, stop_at_tuple, &is_object) < 0) { Py_DECREF(*out_dtype); - if (PyErr_Occurred() && - PyErr_GivenExceptionMatches(PyErr_Occurred(), - PyExc_MemoryError)) { + if (PyErr_Occurred()) { return -1; } - /* Say it's an OBJECT scalar if there's an error */ - PyErr_Clear(); *out_dtype = PyArray_DescrFromType(NPY_OBJECT); if (*out_dtype == NULL) { return -1; diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index f1165999e..2f3575649 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -302,6 +302,35 @@ class TestCreation(TestCase): msg = 'String conversion for %s' % type assert_equal(array(nstr, dtype=type), result, err_msg=msg) + def test_non_sequence_sequence(self): + """Should not segfault. + + Class Fail breaks the sequence protocol for new style classes, i.e., + those derived from object. Class Map is a mapping type indicated by + raising a ValueError. At some point we may raise a warning instead + of an error in the Fail case. + + """ + class Fail(object): + def __len__(self): + return 1 + + def __getitem__(self, index): + raise ValueError() + + class Map(object): + def __len__(self): + return 1 + + def __getitem__(self, index): + raise KeyError() + + a = np.array([Map()]) + assert_(a.shape == (1,)) + assert_(a.dtype == np.dtype(object)) + assert_raises(ValueError, np.array, [Fail()]) + + class TestStructured(TestCase): def test_subarray_field_access(self): a = np.zeros((3, 5), dtype=[('a', ('i4', (2, 2)))]) |