diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2022-06-28 01:06:26 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-28 11:06:26 +0300 |
commit | 6ac7142eedf3edd4e5432b8756ea0b0f6d869998 (patch) | |
tree | 38d52899b630ea2470742257752f011e8b7b33cd /numpy/lib | |
parent | a2caa35dd9f2dae9f492a1af9faa1619e4fcce02 (diff) | |
download | numpy-6ac7142eedf3edd4e5432b8756ea0b0f6d869998.tar.gz |
BUG: Reject non integer array-likes with size 1 in delete (#21857)
Non integer array-likes were not correctly rejected when a new
fast-path was added to `np.delete` in gh-16895.
This includes the _explicitly_ added `dtype=object` which should
not be allowed since it is not allowed in normal indexing either.
Closes gh-21840
Diffstat (limited to 'numpy/lib')
-rw-r--r-- | numpy/lib/function_base.py | 8 | ||||
-rw-r--r-- | numpy/lib/tests/test_function_base.py | 45 |
2 files changed, 39 insertions, 14 deletions
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index d90c23bfe..ca82bb72d 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -5140,10 +5140,14 @@ def delete(arr, obj, axis=None): single_value = False _obj = obj obj = np.asarray(obj) + # `size == 0` to allow empty lists similar to indexing, but (as there) + # is really too generic: if obj.size == 0 and not isinstance(_obj, np.ndarray): obj = obj.astype(intp) - elif obj.size == 1 and not isinstance(_obj, bool): - obj = obj.astype(intp).reshape(()) + elif obj.size == 1 and obj.dtype.kind in "ui": + # For a size 1 integer array we can use the single-value path + # (most dtypes, except boolean, should just fail later). + obj = obj.item() single_value = True if single_value: diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 9cc18a5e4..8457551ca 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -925,18 +925,39 @@ class TestDelete: with pytest.raises(IndexError): np.delete([0, 1, 2], np.array([], dtype=float)) - def test_single_item_array(self): - a_del = delete(self.a, 1) - a_del_arr = delete(self.a, np.array([1])) - a_del_lst = delete(self.a, [1]) - a_del_obj = delete(self.a, np.array([1], dtype=object)) - assert_equal(a_del, a_del_arr, a_del_lst, a_del_obj) - - nd_a_del = delete(self.nd_a, 1, axis=1) - nd_a_del_arr = delete(self.nd_a, np.array([1]), axis=1) - nd_a_del_lst = delete(self.nd_a, [1], axis=1) - nd_a_del_obj = delete(self.nd_a, np.array([1], dtype=object), axis=1) - assert_equal(nd_a_del, nd_a_del_arr, nd_a_del_lst, nd_a_del_obj) + @pytest.mark.parametrize("indexer", [np.array([1]), [1]]) + def test_single_item_array(self, indexer): + a_del_int = delete(self.a, 1) + a_del = delete(self.a, indexer) + assert_equal(a_del_int, a_del) + + nd_a_del_int = delete(self.nd_a, 1, axis=1) + nd_a_del = delete(self.nd_a, np.array([1]), axis=1) + assert_equal(nd_a_del_int, nd_a_del) + + def test_single_item_array_non_int(self): + # Special handling for integer arrays must not affect non-integer ones. + # If `False` was cast to `0` it would delete the element: + res = delete(np.ones(1), np.array([False])) + assert_array_equal(res, np.ones(1)) + + # Test the more complicated (with axis) case from gh-21840 + x = np.ones((3, 1)) + false_mask = np.array([False], dtype=bool) + true_mask = np.array([True], dtype=bool) + + res = delete(x, false_mask, axis=-1) + assert_array_equal(res, x) + res = delete(x, true_mask, axis=-1) + assert_array_equal(res, x[:, :0]) + + # Object or e.g. timedeltas should *not* be allowed + with pytest.raises(IndexError): + delete(np.ones(2), np.array([0], dtype=object)) + + with pytest.raises(IndexError): + # timedeltas are sometimes "integral, but clearly not allowed: + delete(np.ones(2), np.array([0], dtype="m8[ns]")) class TestGradient: |