diff --git a/numpy/core/src/umath/dispatching.c b/numpy/core/src/umath/dispatching.c index 61a95a857e73..8e2f0fe1377a 100644 --- a/numpy/core/src/umath/dispatching.c +++ b/numpy/core/src/umath/dispatching.c @@ -1052,6 +1052,11 @@ logical_ufunc_promoter(PyUFuncObject *NPY_UNUSED(ufunc), /* bail out, this is _only_ to give future/deprecation warning! */ return -1; } + if ((op_dtypes[0] != NULL && PyTypeNum_ISSTRING(op_dtypes[0]->type_num)) + || PyTypeNum_ISSTRING(op_dtypes[1]->type_num)) { + /* bail out on strings: currently casting them to bool is too weird */ + return -1; + } for (int i = 0; i < 3; i++) { PyArray_DTypeMeta *item; diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 5328acea863a..6c56e631590e 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -2134,7 +2134,7 @@ def test_logical_ufuncs_mixed_object_signatures(self, ufunc, signature): [np.logical_and, np.logical_or, np.logical_xor]) def test_logical_ufuncs_support_anything(self, ufunc): # The logical ufuncs support even input that can't be promoted: - a = np.array('1') + a = np.array(b'1', dtype="V3") c = np.array([1., 2.]) assert_array_equal(ufunc(a, c), ufunc([True, True], True)) assert ufunc.reduce(a) == True @@ -2150,6 +2150,23 @@ def test_logical_ufuncs_support_anything(self, ufunc): out = np.zeros((), dtype=a.dtype) assert ufunc.reduce(a, out=out) == 1 + @pytest.mark.parametrize("ufunc", + [np.logical_and, np.logical_or, np.logical_xor]) + def test_logical_ufuncs_reject_string(self, ufunc): + """ + Logical ufuncs are normally well defined by working with the boolean + equivalent, i.e. casting all inputs to bools should work. + + However, casting strings to bools is *currently* weird, because it + actually uses `bool(int(str))`. Thus we explicitly reject strings. + This test should succeed (and can probably just be removed) as soon as + string to bool casts are well defined in NumPy. + """ + with pytest.raises(TypeError, match="contain a loop with signature"): + ufunc(["1"], ["3"]) + with pytest.raises(TypeError, match="contain a loop with signature"): + ufunc.reduce(["1", "2", "0"]) + @pytest.mark.parametrize("ufunc", [np.logical_and, np.logical_or, np.logical_xor]) def test_logical_ufuncs_out_cast_check(self, ufunc):