Skip to content

Commit

Permalink
Merge pull request #21142 from charris/backport-21024
Browse files Browse the repository at this point in the history
API: Disallow strings in logical ufuncs
  • Loading branch information
charris committed Mar 3, 2022
2 parents cfff158 + 87dc319 commit 19f6550
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 1 deletion.
5 changes: 5 additions & 0 deletions numpy/core/src/umath/dispatching.c
Expand Up @@ -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;
Expand Down
19 changes: 18 additions & 1 deletion numpy/core/tests/test_ufunc.py
Expand Up @@ -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
Expand All @@ -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):
Expand Down

0 comments on commit 19f6550

Please sign in to comment.