diff options
| author | Sebastian Berg <sebastian@sipsolutions.net> | 2022-11-28 17:43:13 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-11-28 17:43:13 +0100 |
| commit | 4dc5321a3d8d308808a200a3bf621651a5e67f5a (patch) | |
| tree | c1377cc480147fc402243dc9e5129f5e16b3959a /numpy | |
| parent | c687f2d16f8ba965828fee3a001844b4952474f5 (diff) | |
| download | numpy-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.c | 40 | ||||
| -rw-r--r-- | numpy/core/tests/test_ufunc.py | 14 |
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. |
