Skip to content

Commit

Permalink
Merge pull request #17786 from seberg/bad-array-recursion-error
Browse files Browse the repository at this point in the history
BUG: Raise recursion error during dimension discovery
  • Loading branch information
charris committed Dec 18, 2020
2 parents eeaf12d + 6f9223c commit 8ab99be
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 5 deletions.
39 changes: 34 additions & 5 deletions numpy/core/src/multiarray/ctors.c
Expand Up @@ -741,7 +741,13 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
if (!PySequence_Check(obj) ||
PySequence_Length(obj) < 0) {
*maxndim = 0;
PyErr_Clear();
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_RecursionError) ||
PyErr_ExceptionMatches(PyExc_MemoryError)) {
return -1;
}
PyErr_Clear();
}
return 0;
}

Expand Down Expand Up @@ -780,7 +786,14 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
return 0;
}
else if (PyErr_Occurred()) {
/* TODO[gh-14801]: propagate crashes during attribute access? */
/*
* Clear all but clearly fatal errors (previously cleared all).
* 1.20+ does not clear any errors here.
*/
if (PyErr_ExceptionMatches(PyExc_RecursionError) ||
PyErr_ExceptionMatches(PyExc_MemoryError)) {
return -1;
}
PyErr_Clear();
}

Expand Down Expand Up @@ -1740,7 +1753,8 @@ PyArray_GetArrayParamsFromObject_int(PyObject *op,
else {
*out_dtype = NULL;
if (PyArray_DTypeFromObject(op, NPY_MAXDIMS, out_dtype) < 0) {
if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
if (PyErr_ExceptionMatches(PyExc_MemoryError) ||
PyErr_ExceptionMatches(PyExc_RecursionError)) {
return -1;
}
/* Return NPY_OBJECT for most exceptions */
Expand Down Expand Up @@ -2404,7 +2418,14 @@ PyArray_FromInterface(PyObject *origin)
"__array_interface__");
if (iface == NULL) {
if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
/*
* Clear all but clearly fatal errors (previously cleared all).
* 1.20+ does not clear any errors here.
*/
if (PyErr_ExceptionMatches(PyExc_RecursionError) ||
PyErr_ExceptionMatches(PyExc_MemoryError)) {
return NULL;
}
}
return Py_NotImplemented;
}
Expand Down Expand Up @@ -2672,7 +2693,15 @@ PyArray_FromArrayAttr(PyObject *op, PyArray_Descr *typecode, PyObject *context)
array_meth = PyArray_LookupSpecial_OnInstance(op, "__array__");
if (array_meth == NULL) {
if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
/*
* Clear all but clearly fatal errors (previously cleared all).
* 1.20+ does not clear any errors here.
*/
if (PyErr_ExceptionMatches(PyExc_RecursionError) ||
PyErr_ExceptionMatches(PyExc_MemoryError)) {
return NULL;
}
PyErr_Clear();
}
return Py_NotImplemented;
}
Expand Down
31 changes: 31 additions & 0 deletions numpy/core/tests/test_multiarray.py
Expand Up @@ -818,6 +818,37 @@ def __array__(self, dtype=None):

assert_raises(ValueError, np.array, x())

@pytest.mark.parametrize("error", [RecursionError, MemoryError])
@pytest.mark.parametrize("attribute",
["__array_interface__", "__array__", "__array_struct__"])
def test_bad_array_like_attributes(self, attribute, error):
# Check that errors during attribute retrieval are raised unless
# they are Attribute errors.

class BadInterface:
def __getattr__(self, attr):
if attr == attribute:
raise error
super().__getattr__(attr)

with pytest.raises(error):
np.array(BadInterface())

@pytest.mark.parametrize("error", [RecursionError, MemoryError])
def test_bad_array_like_bad_length(self, error):
# RecursionError and MemoryError are considered "critical" in
# sequences. We could expand this more generally though. (NumPy 1.20)
class BadSequence:
def __len__(self):
raise error
def __getitem__(self):
# must have getitem to be a Sequence
return 1

with pytest.raises(error):
np.array(BadSequence())


def test_from_string(self):
types = np.typecodes['AllInteger'] + np.typecodes['Float']
nstr = ['123', '123']
Expand Down

0 comments on commit 8ab99be

Please sign in to comment.