summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2022-11-28 17:43:13 +0100
committerGitHub <noreply@github.com>2022-11-28 17:43:13 +0100
commit4dc5321a3d8d308808a200a3bf621651a5e67f5a (patch)
treec1377cc480147fc402243dc9e5129f5e16b3959a /numpy
parentc687f2d16f8ba965828fee3a001844b4952474f5 (diff)
downloadnumpy-4dc5321a3d8d308808a200a3bf621651a5e67f5a.tar.gz
ENH: Slightly improve error when gufunc axes has wrong size (#22675)
* DOC: Slightly improve error when gufunc axes has wrong size My hope was to tweak it into something useful that: a @= b can raise when `b` should have two dimensions and has two axes specified but actually only has one. I didn't succeed, but I still think it a slight improvement to give the ufunc name and the actual core dimensions. * ENH: Use AxisError when gufunc axes appear wrong due to the number of entries This allows catching the error relatively targeted for in-place matmul `a @= b` which may use this path. * MAINT: Restore most TypeErrors (a bit more compexl than nice, but...) * DOC: add a release note Co-authored-by: mattip <matti.picus@gmail.com>
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/umath/ufunc_object.c40
-rw-r--r--numpy/core/tests/test_ufunc.py14
2 files changed, 37 insertions, 17 deletions
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 3ac5e7ee6..284af7a54 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -1782,6 +1782,8 @@ _check_keepdims_support(PyUFuncObject *ufunc) {
static int
_parse_axes_arg(PyUFuncObject *ufunc, int op_core_num_dims[], PyObject *axes,
PyArrayObject **op, int broadcast_ndim, int **remap_axis) {
+ static PyObject *AxisError_cls = NULL;
+
int nin = ufunc->nin;
int nop = ufunc->nargs;
int iop, list_size;
@@ -1826,16 +1828,17 @@ _parse_axes_arg(PyUFuncObject *ufunc, int op_core_num_dims[], PyObject *axes,
op_axes_tuple = PyList_GET_ITEM(axes, iop);
if (PyTuple_Check(op_axes_tuple)) {
if (PyTuple_Size(op_axes_tuple) != op_ncore) {
- if (op_ncore == 1) {
- PyErr_Format(PyExc_ValueError,
- "axes item %d should be a tuple with a "
- "single element, or an integer", iop);
- }
- else {
- PyErr_Format(PyExc_ValueError,
- "axes item %d should be a tuple with %d "
- "elements", iop, op_ncore);
+ /* must have been a tuple with too many entries. */
+ npy_cache_import(
+ "numpy.core._exceptions", "AxisError", &AxisError_cls);
+ if (AxisError_cls == NULL) {
+ return -1;
}
+ PyErr_Format(AxisError_cls,
+ "%s: operand %d has %d core dimensions, "
+ "but %zd dimensions are specified by axes tuple.",
+ ufunc_get_name_cstr(ufunc), iop, op_ncore,
+ PyTuple_Size(op_axes_tuple));
return -1;
}
Py_INCREF(op_axes_tuple);
@@ -1847,8 +1850,23 @@ _parse_axes_arg(PyUFuncObject *ufunc, int op_core_num_dims[], PyObject *axes,
}
}
else {
- PyErr_Format(PyExc_TypeError, "axes item %d should be a tuple",
- iop);
+ /* If input is not an integer tell user that a tuple is needed */
+ if (error_converting(PyArray_PyIntAsInt(op_axes_tuple))) {
+ PyErr_Format(PyExc_TypeError,
+ "%s: axes item %d should be a tuple.",
+ ufunc_get_name_cstr(ufunc), iop);
+ return -1;
+ }
+ /* If it is a single integer, inform user that more are needed */
+ npy_cache_import(
+ "numpy.core._exceptions", "AxisError", &AxisError_cls);
+ if (AxisError_cls == NULL) {
+ return -1;
+ }
+ PyErr_Format(AxisError_cls,
+ "%s: operand %d has %d core dimensions, "
+ "but the axes item is a single integer.",
+ ufunc_get_name_cstr(ufunc), iop, op_ncore);
return -1;
}
/*
diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py
index cc6c0839d..00aa48647 100644
--- a/numpy/core/tests/test_ufunc.py
+++ b/numpy/core/tests/test_ufunc.py
@@ -972,10 +972,10 @@ class TestUfunc:
assert_raises(TypeError, inner1d, a, b, axes=[None, 1])
# cannot pass an index unless there is only one dimension
# (output is wrong in this case)
- assert_raises(TypeError, inner1d, a, b, axes=[-1, -1, -1])
+ assert_raises(np.AxisError, inner1d, a, b, axes=[-1, -1, -1])
# or pass in generally the wrong number of axes
- assert_raises(ValueError, inner1d, a, b, axes=[-1, -1, (-1,)])
- assert_raises(ValueError, inner1d, a, b, axes=[-1, (-2, -1), ()])
+ assert_raises(np.AxisError, inner1d, a, b, axes=[-1, -1, (-1,)])
+ assert_raises(np.AxisError, inner1d, a, b, axes=[-1, (-2, -1), ()])
# axes need to have same length.
assert_raises(ValueError, inner1d, a, b, axes=[0, 1])
@@ -1015,14 +1015,16 @@ class TestUfunc:
# list needs to have right length
assert_raises(ValueError, mm, a, b, axes=[])
assert_raises(ValueError, mm, a, b, axes=[(-2, -1)])
- # list should contain tuples for multiple axes
- assert_raises(TypeError, mm, a, b, axes=[-1, -1, -1])
- assert_raises(TypeError, mm, a, b, axes=[(-2, -1), (-2, -1), -1])
+ # list should not contain None, or lists
+ assert_raises(TypeError, mm, a, b, axes=[None, None, None])
assert_raises(TypeError,
mm, a, b, axes=[[-2, -1], [-2, -1], [-2, -1]])
assert_raises(TypeError,
mm, a, b, axes=[(-2, -1), (-2, -1), [-2, -1]])
assert_raises(TypeError, mm, a, b, axes=[(-2, -1), (-2, -1), None])
+ # single integers are AxisErrors if more are required
+ assert_raises(np.AxisError, mm, a, b, axes=[-1, -1, -1])
+ assert_raises(np.AxisError, mm, a, b, axes=[(-2, -1), (-2, -1), -1])
# tuples should not have duplicated values
assert_raises(ValueError, mm, a, b, axes=[(-2, -1), (-2, -1), (-2, -2)])
# arrays should have enough axes.