summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Haldane <allan.haldane@gmail.com>2016-02-16 19:11:30 -0500
committerAllan Haldane <allan.haldane@gmail.com>2016-02-18 23:26:53 -0500
commitbf6017a42dae58f0354e7da70c24f3ded40d0410 (patch)
tree075de7037aa51b57cd9fa5773b6fd92d3657c21b
parent3fb847a93a6d16a8dd466f2545bc8b7d6ea1eb58 (diff)
downloadnumpy-bf6017a42dae58f0354e7da70c24f3ded40d0410.tar.gz
BUG: Segfault for classes with deceptive __len__
Fixes #7264
-rw-r--r--doc/release/1.11.0-notes.rst9
-rw-r--r--numpy/core/src/multiarray/common.c20
-rw-r--r--numpy/core/tests/test_multiarray.py10
3 files changed, 27 insertions, 12 deletions
diff --git a/doc/release/1.11.0-notes.rst b/doc/release/1.11.0-notes.rst
index aa11cdf07..0c60fd175 100644
--- a/doc/release/1.11.0-notes.rst
+++ b/doc/release/1.11.0-notes.rst
@@ -153,6 +153,15 @@ it's unlikely that any third-party code is using them either, but we
mention it here for completeness.
+object dtype detection for old-style classes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In python 2, objects which are instances of old-style user-defined classes no
+longer automatically count as 'object' type in the dtype-detection handler.
+Instead, as in python 3, they may potentially count as sequences, but only if
+they define both a `__len__` and a `__getitem__` method. This fixes a segfault
+and inconsistency between python 2 and 3.
+
New Features
============
diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c
index 5db6b85bd..c216daa95 100644
--- a/numpy/core/src/multiarray/common.c
+++ b/numpy/core/src/multiarray/common.c
@@ -468,9 +468,14 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
/*
* If we reached the maximum recursion depth without hitting one
- * of the above cases, the output dtype should be OBJECT
+ * of the above cases, and obj isn't a sequence-like object, the output
+ * dtype should be either OBJECT or a user-defined type.
+ *
+ * Note that some libraries define sequence-like classes but want them to
+ * 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)) {
+ if (maxdims == 0 || !PySequence_Check(obj) || PySequence_Size(obj) < 0) {
if (*out_dtype == NULL || (*out_dtype)->type_num != NPY_OBJECT) {
Py_XDECREF(*out_dtype);
*out_dtype = PyArray_DescrFromType(NPY_OBJECT);
@@ -481,21 +486,12 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
return 0;
}
- /*
- * If we get here, it may be a sequence. However some libraries define
- * sequence-like classes but want them to be treated as objects, and they
- * expect numpy to treat it as an object if __len__ is not defined.
- */
- size = PySequence_Size(obj);
- if (size < 0) {
- goto fail;
- }
-
/* Recursive case, first check the sequence contains only one type */
seq = PySequence_Fast(obj, "Could not convert object to sequence");
if (seq == NULL) {
goto fail;
}
+ size = PySequence_Fast_GET_SIZE(seq);
objects = PySequence_Fast_ITEMS(seq);
common_type = size > 0 ? Py_TYPE(objects[0]) : NULL;
for (i = 1; i < size; ++i) {
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index d57e7c106..f16c74258 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -702,6 +702,16 @@ class TestCreation(TestCase):
d = np.array([Point2(), Point2(), Point2()])
assert_equal(d.dtype, np.dtype(object))
+ def test_false_len_sequence(self):
+ # gh-7264, segfault for this example
+ class C:
+ def __getitem__(self, i):
+ raise IndexError
+ def __len__(self):
+ return 42
+
+ assert_raises(ValueError, np.array, C()) # segfault?
+
class TestStructured(TestCase):
def test_subarray_field_access(self):