summaryrefslogtreecommitdiff
path: root/numpy/lib
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2022-06-28 01:06:26 -0700
committerGitHub <noreply@github.com>2022-06-28 11:06:26 +0300
commit6ac7142eedf3edd4e5432b8756ea0b0f6d869998 (patch)
tree38d52899b630ea2470742257752f011e8b7b33cd /numpy/lib
parenta2caa35dd9f2dae9f492a1af9faa1619e4fcce02 (diff)
downloadnumpy-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.py8
-rw-r--r--numpy/lib/tests/test_function_base.py45
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: