Skip to content

Commit

Permalink
BUG: Reject non integer array-likes with size 1 in delete (numpy#21857)
Browse files Browse the repository at this point in the history
Non integer array-likes were not correctly rejected when a new
fast-path was added to `np.delete` in numpygh-16895.
This includes the _explicitly_ added `dtype=object` which should
not be allowed since it is not allowed in normal indexing either.

Closes numpygh-21840
  • Loading branch information
seberg authored Jun 28, 2022
1 parent a2caa35 commit 6ac7142
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 14 deletions.
8 changes: 6 additions & 2 deletions numpy/lib/function_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
45 changes: 33 additions & 12 deletions numpy/lib/tests/test_function_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -925,18 +925,39 @@ def test_index_floats(self):
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:
Expand Down

0 comments on commit 6ac7142

Please sign in to comment.