8000 Pandas fix by charris · Pull Request #50 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

Pandas fix #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
9 commits merged into from
Mar 10, 2011
52 changes: 43 additions & 9 deletions numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
29 changes: 29 additions & 0 deletions numpy/core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,35 @@ def test_from_string(self) :
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)))])
Expand Down
0