Skip to content
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

BUG: clear only attribute errors in get_attr_string.h::maybe_get_attr #14745

Merged
merged 11 commits into from
Oct 30, 2019
5 changes: 4 additions & 1 deletion numpy/core/src/common/binop_override.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,14 @@ binop_should_defer(PyObject *self, PyObject *other, int inplace)
* check whether __array_ufunc__ equals None.
*/
attr = PyArray_LookupSpecial(other, "__array_ufunc__");
if (attr) {
if (attr != NULL) {
defer = !inplace && (attr == Py_None);
Py_DECREF(attr);
return defer;
}
else if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}
/*
* Otherwise, we need to check for the legacy __array_priority__. But if
* other.__class__ is a subtype of self.__class__, then it's already had
Expand Down
20 changes: 8 additions & 12 deletions numpy/core/src/common/get_attr_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,14 @@ _is_basic_python_type(PyTypeObject *tp)
}

/*
* Stripped down version of PyObject_GetAttrString,
* avoids lookups for None, tuple, and List objects,
* and doesn't create a PyErr since this code ignores it.
* Stripped down version of PyObject_GetAttrString(obj, name) that does not
* raise PyExc_AttributeError.
*
* This can be much faster then PyObject_GetAttrString where
* exceptions are not used by caller.
* This allows it to avoid creating then discarding exception objects when
* performing lookups on objects without any attributes.
*
* 'obj' is the object to search for attribute.
*
* 'name' is the attribute to search for.
*
* Returns attribute value on success, NULL on failure.
* Returns attribute value on success, NULL without an exception set if
* there is no such attribute, and NULL with an exception on failure.
*/
static NPY_INLINE PyObject *
maybe_get_attr(PyObject *obj, char *name)
Expand All @@ -62,7 +58,7 @@ maybe_get_attr(PyObject *obj, char *name)
/* Attribute referenced by (char *)name */
if (tp->tp_getattr != NULL) {
res = (*tp->tp_getattr)(obj, name);
if (res == NULL) {
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
eric-wieser marked this conversation as resolved.
Show resolved Hide resolved
}
}
Expand All @@ -78,7 +74,7 @@ maybe_get_attr(PyObject *obj, char *name)
}
res = (*tp->tp_getattro)(obj, w);
Py_DECREF(w);
if (res == NULL) {
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
}
}
Expand Down
3 changes: 3 additions & 0 deletions numpy/core/src/common/ufunc_override.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ PyUFuncOverride_GetNonDefaultArrayUfunc(PyObject *obj)
*/
cls_array_ufunc = PyArray_LookupSpecial(obj, "__array_ufunc__");
if (cls_array_ufunc == NULL) {
if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}
return NULL;
}
/* Ignore if the same as ndarray.__array_ufunc__ */
Expand Down
8 changes: 7 additions & 1 deletion numpy/core/src/multiarray/arrayfunction_override.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ static PyObject *
get_array_function(PyObject *obj)
{
static PyObject *ndarray_array_function = NULL;
static PyObject *array_function;
keewis marked this conversation as resolved.
Show resolved Hide resolved

if (ndarray_array_function == NULL) {
ndarray_array_function = get_ndarray_array_function();
Expand All @@ -37,7 +38,12 @@ get_array_function(PyObject *obj)
return ndarray_array_function;
}

return PyArray_LookupSpecial(obj, "__array_function__");
array_function = PyArray_LookupSpecial(obj, "__array_function__");
if (array_function == NULL && PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}

return array_function;
}


Expand Down
10 changes: 10 additions & 0 deletions numpy/core/src/multiarray/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,10 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
}
Py_DECREF(ip);
}
else if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}


/* The array struct interface */
ip = PyArray_LookupSpecial_OnInstance(obj, "__array_struct__");
Expand All @@ -389,6 +393,9 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
}
Py_DECREF(ip);
}
else if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}

/* The old buffer interface */
#if !defined(NPY_PY3K)
Expand Down Expand Up @@ -419,6 +426,9 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims,
goto fail;
}
}
else if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}

/*
* If we reached the maximum recursion depth without hitting one
Expand Down
19 changes: 18 additions & 1 deletion numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,10 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
return 0;
}
}
else if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}


/* obj has the __array_interface__ interface */
e = PyArray_LookupSpecial_OnInstance(obj, "__array_interface__");
Expand Down Expand Up @@ -881,6 +885,9 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
return 0;
}
}
else if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}

seq = PySequence_Fast(obj, "Could not convert object to sequence");
if (seq == NULL) {
Expand Down Expand Up @@ -2351,7 +2358,11 @@ PyArray_FromStructInterface(PyObject *input)

attr = PyArray_LookupSpecial_OnInstance(input, "__array_struct__");
if (attr == NULL) {
return Py_NotImplemented;
if (PyErr_Occurred()) {
return NULL;
} else {
return Py_NotImplemented;
}
eric-wieser marked this conversation as resolved.
Show resolved Hide resolved
}
if (!NpyCapsule_Check(attr)) {
goto fail;
Expand Down Expand Up @@ -2463,6 +2474,9 @@ PyArray_FromInterface(PyObject *origin)
iface = PyArray_LookupSpecial_OnInstance(origin,
"__array_interface__");
if (iface == NULL) {
if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}
return Py_NotImplemented;
}
if (!PyDict_Check(iface)) {
Expand Down Expand Up @@ -2716,6 +2730,9 @@ 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? */
}
return Py_NotImplemented;
}
if (context == NULL) {
Expand Down
7 changes: 5 additions & 2 deletions numpy/core/src/multiarray/multiarraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ PyArray_GetPriority(PyObject *obj, double default_)

ret = PyArray_LookupSpecial_OnInstance(obj, "__array_priority__");
if (ret == NULL) {
if (PyErr_Occurred()) {
PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
}
return default_;
}

Expand Down Expand Up @@ -2063,7 +2066,7 @@ array_fromfile(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds)
if (file == NULL) {
return NULL;
}

if (offset != 0 && strcmp(sep, "") != 0) {
PyErr_SetString(PyExc_TypeError, "'offset' argument only permitted for binary files");
Py_XDECREF(type);
Expand Down Expand Up @@ -3265,7 +3268,7 @@ array_datetime_data(PyObject *NPY_UNUSED(dummy), PyObject *args)
}

meta = get_datetime_metadata_from_dtype(dtype);
Py_DECREF(dtype);
Py_DECREF(dtype);
if (meta == NULL) {
return NULL;
}
Expand Down
29 changes: 29 additions & 0 deletions numpy/core/tests/test_issue14735.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pytest
import warnings
import numpy as np


class Wrapper:
def __init__(self, array):
self.array = array

def __len__(self):
return len(self.array)

def __getitem__(self, item):
return type(self)(self.array[item])

def __getattr__(self, name):
if name.startswith("__array_"):
warnings.warn("object got converted", UserWarning, stacklevel=1)

return getattr(self.array, name)

def __repr__(self):
return "<Wrapper({self.array})>".format(self=self)

@pytest.mark.filterwarnings("error")
def test_getattr_warning():
array = Wrapper(np.arange(10))
with pytest.raises(UserWarning, match="object got converted"):
np.asarray(array)