-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
MAINT: Use the same exception for all bad axis requests #8584
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
564b7cb
763589d
370b650
eb642f1
8d6ec65
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 |
---|---|---|
|
@@ -134,6 +134,42 @@ check_and_adjust_index(npy_intp *index, npy_intp max_item, int axis, | |
return 0; | ||
} | ||
|
||
/* | ||
* Returns -1 and sets an exception if *axis is an invalid axis for | ||
* an array of dimension ndim, otherwise adjusts it in place to be | ||
* 0 <= *axis < ndim, and returns 0. | ||
*/ | ||
static NPY_INLINE int | ||
check_and_adjust_axis(int *axis, int ndim) | ||
{ | ||
/* Check that index is valid, taking into account negative indices */ | ||
if (NPY_UNLIKELY((*axis < -ndim) || (*axis >= ndim))) { | ||
/* | ||
* Load the exception type, if we don't already have it. Unfortunately | ||
* we don't have access to npy_cache_import here | ||
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. More for my education than anything else, is there a reason not just to 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. This is a public header, but I think That could be fixed by moving this to 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. Since every problem supposedly is solvable with another layer of redirection, could one call an error-producing function in 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. Arguably, the inlining is not important here, as this isn't nearly as critical a path as 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. OK, how about then moving it to 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. Main argument for leaving it here is it gives clear contrast with 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. True; fine to keep as is too even if it duplicates a bit of code. 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. @eric-wieser did you try to include 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 think the problem is that something outside numpy/core/src tries to include "common.h", which then doesn't have access to npy_include. I remember it not building, but I forget the exact error. 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. And just to be clear, the "public" headers are in |
||
*/ | ||
static PyObject *AxisError_cls = NULL; | ||
if (AxisError_cls == NULL) { | ||
PyObject *mod = PyImport_ImportModule("numpy.core._internal"); | ||
|
||
if (mod != NULL) { | ||
AxisError_cls = PyObject_GetAttrString(mod, "AxisError"); | ||
Py_DECREF(mod); | ||
} | ||
} | ||
|
||
PyErr_Format(AxisError_cls, | ||
"axis %d is out of bounds for array of dimension %d", | ||
*axis, ndim); | ||
return -1; | ||
} | ||
/* adjust negative indices */ | ||
if (*axis < 0) { | ||
*axis += ndim; | ||
} | ||
return 0; | ||
} | ||
|
||
|
||
/* | ||
* return true if pointer is aligned to 'alignment' | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -259,17 +259,10 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) | |
PyObject *tmp = PyTuple_GET_ITEM(axis_in, i); | ||
int axis = PyArray_PyIntAsInt_ErrMsg(tmp, | ||
"integers are required for the axis tuple elements"); | ||
int axis_orig = axis; | ||
if (error_converting(axis)) { | ||
return NPY_FAIL; | ||
} | ||
if (axis < 0) { | ||
axis += ndim; | ||
} | ||
if (axis < 0 || axis >= ndim) { | ||
PyErr_Format(PyExc_ValueError, | ||
"'axis' entry %d is out of bounds [-%d, %d)", | ||
axis_orig, ndim, ndim); | ||
if (check_and_adjust_axis(&axis, ndim) < 0) { | ||
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. Here and below: check for status using |
||
return NPY_FAIL; | ||
} | ||
if (out_axis_flags[axis]) { | ||
|
@@ -284,20 +277,16 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) | |
} | ||
/* Try to interpret axis as an integer */ | ||
else { | ||
int axis, axis_orig; | ||
int axis; | ||
|
||
memset(out_axis_flags, 0, ndim); | ||
|
||
axis = PyArray_PyIntAsInt_ErrMsg(axis_in, | ||
"an integer is required for the axis"); | ||
axis_orig = axis; | ||
|
||
if (error_converting(axis)) { | ||
return NPY_FAIL; | ||
B41A } | ||
if (axis < 0) { | ||
axis += ndim; | ||
} | ||
/* | ||
* Special case letting axis={-1,0} slip through for scalars, | ||
* for backwards compatibility reasons. | ||
|
@@ -306,10 +295,7 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) | |
return NPY_SUCCEED; | ||
} | ||
|
||
if (axis < 0 || axis >= ndim) { | ||
PyErr_Format(PyExc_ValueError, | ||
"'axis' entry %d is out of bounds [-%d, %d)", | ||
axis_orig, ndim, ndim); | ||
if (check_and_adjust_axis(&axis, ndim) < 0) { | ||
return NPY_FAIL; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -327,7 +327,6 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis) | |
PyArray_Descr *dtype = NULL; | ||
PyArrayObject *ret = NULL; | ||
PyArrayObject_fields *sliding_view = NULL; | ||
int orig_axis = axis; | ||
|
||
if (narrays <= 0) { | ||
PyErr_SetString(PyExc_ValueError, | ||
|
@@ -345,13 +344,7 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis) | |
} | ||
|
||
/* Handle standard Python negative indexing */ | ||
if (axis < 0) { | ||
axis += ndim; | ||
} | ||
|
||
if (axis < 0 || axis >= ndim) { | ||
PyErr_Format(PyExc_IndexError, | ||
"axis %d out of bounds [0, %d)", orig_axis, ndim); | ||
if (check_and_adjust_axis(&axis, ndim) < 0) { | ||
return NULL; | ||
} | ||
|
||
|
@@ -4109,6 +4102,24 @@ array_may_share_memory(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject * | |
return array_shares_memory_impl(args, kwds, NPY_MAY_SHARE_BOUNDS, 0); | ||
} | ||
|
||
static PyObject * | ||
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 presume this is for follow up in python code? If so, I think it should be part of a follow-up PR, not be done here (if only to give a chance to discuss whether it is best to go through 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've added the python stuff to this PR now... |
||
normalize_axis_index(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) | ||
{ | ||
static char *kwlist[] = {"axis", "ndim", NULL}; | ||
int axis; | ||
int ndim; | ||
|
||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwlist, | ||
&axis, &ndim)) { | ||
return NULL; | ||
} | ||
|
||
if(check_and_adjust_axis(&axis, ndim) < 0) { | ||
return NULL; | ||
} | ||
|
||
return PyInt_FromLong(axis); | ||
} | ||
|
||
static struct PyMethodDef array_module_methods[] = { | ||
{"_get_ndarray_c_version", |
|
@@ -4284,6 +4295,8 @@ static struct PyMethodDef array_module_methods[] = { |
METH_VARARGS | METH_KEYWORDS, NULL}, | ||
{"unpackbits", (PyCFunction)io_unpack, | ||
METH_VARARGS | METH_KEYWORDS, NULL}, | ||
{"normalize_axis_index", (PyCFunction)normalize_axis_index, | ||
METH_VARARGS | METH_KEYWORDS, NULL}, | ||
{NULL, NULL, 0, NULL} /* sentinel */ | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -705,12 +705,7 @@ PyArray_Transpose(PyArrayObject *ap, PyArray_Dims *permute) | |
} | ||
for (i = 0; i < n; i++) { | ||
axis = axes[i]; | ||
if (axis < 0) { | ||
axis = PyArray_NDIM(ap) + axis; | ||
} | ||
if (axis < 0 || axis >= PyArray_NDIM(ap)) { | ||
PyErr_SetString(PyExc_ValueError, | ||
"invalid axis for this array"); | ||
if (check_and_adjust_axis(&axis, PyArray_NDIM(ap)) < 0) { | ||
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.
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. Maybe useful to double check this everywhere. 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 do you think I should accept in |
||
return NULL; | ||
} | ||
if (reverse_permutation[axis] != -1) { | ||
|
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.
adjust
seems to imply something always happens; how aboutcheck_and_normalize_axis
. Or justnormalize_axis
?