Skip to content

Commit 75b5bf1

Browse files
authored
Merge pull request numpy#26268 from mtsokol/nonzero-0d-input
API: Disallow 0D input arrays in ``nonzero``
2 parents 3ea613c + eb3db92 commit 75b5bf1

File tree

5 files changed

+24
-56
lines changed

5 files changed

+24
-56
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Scalars and 0D arrays are disallowed for `numpy.nonzero` and `numpy.ndarray.nonzero`.

numpy/_core/src/multiarray/item_selection.c

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2797,6 +2797,23 @@ NPY_NO_EXPORT PyObject *
27972797
PyArray_Nonzero(PyArrayObject *self)
27982798
{
27992799
int i, ndim = PyArray_NDIM(self);
2800+
if (ndim == 0) {
2801+
char const* msg;
2802+
if (PyArray_ISBOOL(self)) {
2803+
msg =
2804+
"Calling nonzero on 0d arrays is not allowed. "
2805+
"Use np.atleast_1d(scalar).nonzero() instead. "
2806+
"If the context of this error is of the form "
2807+
"`arr[nonzero(cond)]`, just use `arr[cond]`.";
2808+
} else {
2809+
msg =
2810+
"Calling nonzero on 0d arrays is not allowed. "
2811+
"Use np.atleast_1d(scalar).nonzero() instead.";
2812+
}
2813+
PyErr_SetString(PyExc_ValueError, msg);
2814+
return NULL;
2815+
}
2816+
28002817
PyArrayObject *ret = NULL;
28012818
PyObject *ret_tuple;
28022819
npy_intp ret_dims[2];
@@ -2818,42 +2835,6 @@ PyArray_Nonzero(PyArrayObject *self)
28182835
nonzero = PyDataType_GetArrFuncs(dtype)->nonzero;
28192836
needs_api = PyDataType_FLAGCHK(dtype, NPY_NEEDS_PYAPI);
28202837

2821-
/* Special case - nonzero(zero_d) is nonzero(atleast_1d(zero_d)) */
2822-
if (ndim == 0) {
2823-
char const* msg;
2824-
if (PyArray_ISBOOL(self)) {
2825-
msg =
2826-
"Calling nonzero on 0d arrays is deprecated, as it behaves "
2827-
"surprisingly. Use `atleast_1d(cond).nonzero()` if the old "
2828-
"behavior was intended. If the context of this warning is of "
2829-
"the form `arr[nonzero(cond)]`, just use `arr[cond]`.";
2830-
}
2831-
else {
2832-
msg =
2833-
"Calling nonzero on 0d arrays is deprecated, as it behaves "
2834-
"surprisingly. Use `atleast_1d(arr).nonzero()` if the old "
2835-
"behavior was intended.";
2836-
}
2837-
if (DEPRECATE(msg) < 0) {
2838-
return NULL;
2839-
}
2840-
2841-
static npy_intp const zero_dim_shape[1] = {1};
2842-
static npy_intp const zero_dim_strides[1] = {0};
2843-
2844-
Py_INCREF(PyArray_DESCR(self)); /* array creation steals reference */
2845-
PyArrayObject *self_1d = (PyArrayObject *)PyArray_NewFromDescrAndBase(
2846-
Py_TYPE(self), PyArray_DESCR(self),
2847-
1, zero_dim_shape, zero_dim_strides, PyArray_BYTES(self),
2848-
PyArray_FLAGS(self), (PyObject *)self, (PyObject *)self);
2849-
if (self_1d == NULL) {
2850-
return NULL;
2851-
}
2852-
ret_tuple = PyArray_Nonzero(self_1d);
2853-
Py_DECREF(self_1d);
2854-
return ret_tuple;
2855-
}
2856-
28572838
/*
28582839
* First count the number of non-zeros in 'self'.
28592840
*/

numpy/_core/tests/test_deprecations.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,6 @@ def test_deprecate_unparsable_string(self, invalid_str):
283283
assert_array_equal(res, x)
284284

285285

286-
class TestNonZero(_DeprecationTestCase):
287-
# 2019-05-26, 1.17.0
288-
def test_zerod(self):
289-
self.assert_deprecated(lambda: np.nonzero(np.array(0)))
290-
self.assert_deprecated(lambda: np.nonzero(np.array(1)))
291-
292-
293286
class TestToString(_DeprecationTestCase):
294287
# 2020-03-06 1.19.0
295288
message = re.escape("tostring() is deprecated. Use tobytes() instead.")

numpy/_core/tests/test_numeric.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,16 +1567,12 @@ def test_nonzero_trivial(self):
15671567
assert_equal(np.count_nonzero(np.array([1], dtype='?')), 1)
15681568
assert_equal(np.nonzero(np.array([1])), ([0],))
15691569

1570-
def test_nonzero_zerod(self):
1571-
assert_equal(np.count_nonzero(np.array(0)), 0)
1572-
assert_equal(np.count_nonzero(np.array(0, dtype='?')), 0)
1573-
with assert_warns(DeprecationWarning):
1574-
assert_equal(np.nonzero(np.array(0)), ([],))
1575-
1576-
assert_equal(np.count_nonzero(np.array(1)), 1)
1577-
assert_equal(np.count_nonzero(np.array(1, dtype='?')), 1)
1578-
with assert_warns(DeprecationWarning):
1579-
assert_equal(np.nonzero(np.array(1)), ([0],))
1570+
def test_nonzero_zerodim(self):
1571+
err_msg = "Calling nonzero on 0d arrays is not allowed"
1572+
with assert_raises_regex(ValueError, err_msg):
1573+
np.nonzero(np.array(0))
1574+
with assert_raises_regex(ValueError, err_msg):
1575+
np.array(1).nonzero()
15801576

15811577
def test_nonzero_onedim(self):
15821578
x = np.array([1, 0, 2, -1, 0, 0, 8])

tools/ci/array-api-skips.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ array_api_tests/test_signatures.py::test_func_signature[reshape]
1414
array_api_tests/test_signatures.py::test_func_signature[argsort]
1515
array_api_tests/test_signatures.py::test_func_signature[sort]
1616

17-
# nonzero for 0D should error
18-
array_api_tests/test_searching_functions.py::test_nonzero_zerodim_error
19-
2017
# TODO: check why in CI `inspect.signature(np.vecdot)` returns (*arg, **kwarg)
2118
# instead of raising ValueError. mtsokol: couldn't reproduce locally
2219
array_api_tests/test_signatures.py::test_func_signature[vecdot]

0 commit comments

Comments
 (0)