summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2011-03-09 19:46:53 -0700
committerCharles Harris <charlesr.harris@gmail.com>2011-03-09 19:46:53 -0700
commitb94585c25f81d0d90d25d1ce9b2781f478606734 (patch)
tree3c59c78a2a09b035969fcb074fc86e3b7ebb32a0
parentce9fea2c202f80ab39fa2b4866efd08ad075b9f2 (diff)
parent4aaa4b7e500514a3bc37b515445592d17dce65b1 (diff)
downloadnumpy-b94585c25f81d0d90d25d1ce9b2781f478606734.tar.gz
Merge branch 'pandas-fix'
-rw-r--r--numpy/core/src/multiarray/ctors.c52
-rw-r--r--numpy/core/tests/test_multiarray.py29
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)))])