Skip to content

Commit

Permalink
MNT: raise TypeError instead of RuntimeError after finding ambiguous …
Browse files Browse the repository at this point in the history
…promoters
  • Loading branch information
ngoldbaum committed Mar 21, 2024
1 parent 7f1c8cb commit f7c8a67
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 19 deletions.
12 changes: 8 additions & 4 deletions numpy/_core/_exceptions.py
Expand Up @@ -38,26 +38,30 @@ def __init__(self, ufunc):
@_display_as_base
class _UFuncNoLoopError(UFuncTypeError):
""" Thrown when a ufunc loop cannot be found """
def __init__(self, ufunc, dtypes):
def __init__(self, ufunc, dtypes, ambiguous_promoter):
super().__init__(ufunc)
self.dtypes = tuple(dtypes)
self.ambiguous_promoter = ambiguous_promoter

def __str__(self):
return (
err = (
"ufunc {!r} did not contain a loop with signature matching types "
"{!r} -> {!r}"
"{!r} -> {!r}."
).format(
self.ufunc.__name__,
_unpack_tuple(self.dtypes[:self.ufunc.nin]),
_unpack_tuple(self.dtypes[self.ufunc.nin:])
)
if self.ambiguous_promoter:
err += " (ambiguous promoters found)"
return err


@_display_as_base
class _UFuncBinaryResolutionError(_UFuncNoLoopError):
""" Thrown when a binary resolution fails """
def __init__(self, ufunc, dtypes):
super().__init__(ufunc, dtypes)
super().__init__(ufunc, dtypes, False)
assert len(self.dtypes) == 2

def __str__(self):
Expand Down
13 changes: 3 additions & 10 deletions numpy/_core/src/umath/dispatching.c
Expand Up @@ -447,14 +447,7 @@ resolve_implementation_info(PyUFuncObject *ufunc,
PyObject *given = PyArray_TupleFromItems(
ufunc->nargs, (PyObject **)op_dtypes, 1);
if (given != NULL) {
PyErr_Format(PyExc_RuntimeError,
"Could not find a loop for the inputs:\n %S\n"
"The two promoters %S and %S matched the input "
"equally well. Promoters must be designed "
"to be unambiguous. NOTE: This indicates an error "
"in NumPy or an extending library and should be "
"reported.",
given, best_dtypes, curr_dtypes);
raise_no_loop_found_error(ufunc, (PyObject **)op_dtypes, 1);
Py_DECREF(given);
}
*out_info = NULL;
Expand Down Expand Up @@ -1053,7 +1046,7 @@ promote_and_get_ufuncimpl(PyUFuncObject *ufunc,
handle_error:
/* We only set the "no loop found error here" */
if (!PyErr_Occurred()) {
raise_no_loop_found_error(ufunc, (PyObject **)op_dtypes);
raise_no_loop_found_error(ufunc, (PyObject **)op_dtypes, 0);
}
/*
* Otherwise an error occurred, but if the error was DTypePromotionError
Expand All @@ -1063,7 +1056,7 @@ promote_and_get_ufuncimpl(PyUFuncObject *ufunc,
else if (PyErr_ExceptionMatches(npy_DTypePromotionError)) {
PyObject *err_type = NULL, *err_value = NULL, *err_traceback = NULL;
PyErr_Fetch(&err_type, &err_value, &err_traceback);
raise_no_loop_found_error(ufunc, (PyObject **)op_dtypes);
raise_no_loop_found_error(ufunc, (PyObject **)op_dtypes, 0);
npy_PyErr_ChainExceptionsCause(err_type, err_value, err_traceback);
}
return NULL;
Expand Down
9 changes: 5 additions & 4 deletions numpy/_core/src/umath/ufunc_type_resolution.c
Expand Up @@ -106,7 +106,7 @@ raise_binary_type_reso_error(PyUFuncObject *ufunc, PyArrayObject **operands) {
*/
NPY_NO_EXPORT int
raise_no_loop_found_error(
PyUFuncObject *ufunc, PyObject **dtypes)
PyUFuncObject *ufunc, PyObject **dtypes, int ambiguous_promoter)
{
static PyObject *exc_type = NULL;

Expand All @@ -122,7 +122,8 @@ raise_no_loop_found_error(
return -1;
}
/* produce an error object */
PyObject *exc_value = PyTuple_Pack(2, ufunc, dtypes_tup);
PyObject *exc_value = PyTuple_Pack(3, ufunc, dtypes_tup,
ambiguous_promoter ? Py_True : Py_False);
Py_DECREF(dtypes_tup);
if (exc_value == NULL) {
return -1;
Expand Down Expand Up @@ -553,7 +554,7 @@ PyUFunc_SimpleUniformOperationTypeResolver(
out_dtypes[iop] = PyArray_DESCR(operands[iop]);
Py_INCREF(out_dtypes[iop]);
}
raise_no_loop_found_error(ufunc, (PyObject **)out_dtypes);
raise_no_loop_found_error(ufunc, (PyObject **)out_dtypes, 0);
for (iop = 0; iop < ufunc->nin; iop++) {
Py_DECREF(out_dtypes[iop]);
out_dtypes[iop] = NULL;
Expand Down Expand Up @@ -1578,7 +1579,7 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc,
types += nargs;
}

return raise_no_loop_found_error(ufunc, (PyObject **)dtypes);
return raise_no_loop_found_error(ufunc, (PyObject **)dtypes, 0);
}


Expand Down
2 changes: 1 addition & 1 deletion numpy/_core/src/umath/ufunc_type_resolution.h
Expand Up @@ -140,6 +140,6 @@ PyUFunc_DefaultLegacyInnerLoopSelector(PyUFuncObject *ufunc,
int *out_needs_api);

NPY_NO_EXPORT int
raise_no_loop_found_error(PyUFuncObject *ufunc, PyObject **dtypes);
raise_no_loop_found_error(PyUFuncObject *ufunc, PyObject **dtypes, int ambiguous_promoter);

#endif
6 changes: 6 additions & 0 deletions numpy/_core/tests/test_stringdtype.py
Expand Up @@ -765,6 +765,12 @@ def test_multiply_reduce():
assert res == val * np.prod(repeats)


def test_multiply_two_string_raises():
arr = np.array(["hello", "world"])
with pytest.raises(TypeError):
np.multiply(arr, arr)


@pytest.mark.parametrize("use_out", [True, False])
@pytest.mark.parametrize("other", [2, [2, 1, 3, 4, 1, 3]])
@pytest.mark.parametrize(
Expand Down

0 comments on commit f7c8a67

Please sign in to comment.