-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
MAINT: more informative errors for 'axis' argument of ufuncs #5201
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3745,7 +3745,16 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, | |
for (i = 0; i < naxes; ++i) { | ||
PyObject *tmp = PyTuple_GET_ITEM(axes_in, i); | ||
int axis = PyArray_PyIntAsInt(tmp); | ||
if (axis == -1 && PyErr_Occurred()) { | ||
int axis_orig = axis; | ||
PyObject *error = PyErr_Occurred(); | ||
if (axis == -1 && error != NULL) { ; | ||
/* | ||
* Need to re-raise the exact same exception returned by | ||
* PyArray_PyIntAsInt to not break the deprecation tests | ||
*/ | ||
PyErr_Clear(); | ||
PyErr_SetString(error, | ||
"'axis' tuple entries must be integers"); | ||
Py_XDECREF(otype); | ||
Py_DECREF(mp); | ||
return NULL; | ||
|
@@ -3754,8 +3763,9 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, | |
axis += ndim; | ||
} | ||
if (axis < 0 || axis >= ndim) { | ||
PyErr_SetString(PyExc_ValueError, | ||
"'axis' entry is out of bounds"); | ||
PyErr_Format(PyExc_ValueError, | ||
"'axis' entry %d is out of bounds [-%d, %d)", | ||
axis_orig, ndim, ndim); | ||
Py_XDECREF(otype); | ||
Py_DECREF(mp); | ||
return NULL; | ||
|
@@ -3766,8 +3776,17 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, | |
/* Try to interpret axis as an integer */ | ||
else { | ||
int axis = PyArray_PyIntAsInt(axes_in); | ||
int axis_orig = axis; | ||
PyObject *error = PyErr_Occurred(); | ||
/* TODO: PyNumber_Index would be good to use here */ | ||
if (axis == -1 && PyErr_Occurred()) { | ||
if (axis == -1 && error != NULL) { | ||
/* | ||
* Need to re-raise the exact same exception returned by | ||
* PyArray_PyIntAsInt to not break the deprecation tests | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which deprecation tests? Are those deprecation tests still valid? Some deprecations have reached their due date. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I ask, because I'd rather avoid temporary complications in the code like this that may never get resimp ED48 lified. |
||
*/ | ||
PyErr_Clear(); | ||
PyErr_SetString(error, | ||
"'axis' must be None, an integer or a tuple of integers"); | ||
Py_XDECREF(otype); | ||
Py_DECREF(mp); | ||
return NULL; | ||
|
@@ -3780,8 +3799,9 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, | |
axis = 0; | ||
} | ||
else if (axis < 0 || axis >= ndim) { | ||
PyErr_SetString(PyExc_ValueError, | ||
"'axis' entry is out of bounds"); | ||
PyErr_Format(PyExc_ValueError, | ||
"'axis' entry %d is out of bounds [-%d, %d)", | ||
axis_orig, ndim, ndim); | ||
Py_XDECREF(otype); | ||
Py_DECREF(mp); | ||
return NULL; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what tests are these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was these that were failing. By default
assert_deprecated
is looking for aDeprecationWarning
, but it was getting aTypeError
. We could fool around with the tests to make them pass and have a more direct error raising here, but it is probably not that bad of a thing that we re-raise whatPyArray_PyIntAsInt
is raising.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, I didn't bother with such replacement in a lot of places personally. It can be nice to show the deprecation error I guess, but not sure if it is worth juggling around in c-code to get there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing the test is easy, there is an argument in that function which you can tell that the error is a TypeError and not a Deprecation one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this again, the comment seems out of place. But I now more strongly think that we cannot unconditionally raise a
TypeError
, especially after considering what happens with integer overflows. On my 32 bit numpy I get:Which are anything but informative! 64 bit systems with 4 byte ints could also show a
ValueError
, if the value overflows anint
but not anpy_intp
.The proper thing to do would be to call
PyArray_IntAsInt_ErrMsg
directly, not throughPyArray_IntAsInt
, but since it is not part of the API I am not sure of what is best here: checking the error type and lettingValueError
andOverflowError
pass unchanged, and modify the message for other errors feels hacky and fragile, but seems the only other way to correctness.A more elaborate possibility is to add
PyArray_ConvertMultiAxis
to the API, and adapt it slightly (returnnaxes
also) so that it can be used here directly.Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a serious concern? the largest possible axis index is 31... (not
2**31
, just 31).I'll point out that this is Yet Another case where we are considering exposing boring utility functions with funky calling conventions in the API solely so we can use them internally in both multiarray and umath.