diff options
author | Allan Haldane <allan.haldane@gmail.com> | 2016-02-16 19:11:30 -0500 |
---|---|---|
committer | Allan Haldane <allan.haldane@gmail.com> | 2016-02-18 23:26:53 -0500 |
commit | bf6017a42dae58f0354e7da70c24f3ded40d0410 (patch) | |
tree | 075de7037aa51b57cd9fa5773b6fd92d3657c21b | |
parent | 3fb847a93a6d16a8dd466f2545bc8b7d6ea1eb58 (diff) | |
download | numpy-bf6017a42dae58f0354e7da70c24f3ded40d0410.tar.gz |
BUG: Segfault for classes with deceptive __len__
Fixes #7264
-rw-r--r-- | doc/release/1.11.0-notes.rst | 9 | ||||
-rw-r--r-- | numpy/core/src/multiarray/common.c | 20 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 10 |
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): |