Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions numpy/_core/_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,39 @@ def _clip(a, min=None, max=None, out=None, **kwargs):
if min is None and max is None:
raise ValueError("One of max or min must be given")

if min is None:
return um.minimum(a, max, out=out, **kwargs)
elif max is None:
return um.maximum(a, min, out=out, **kwargs)
else:
return um.clip(a, min, max, out=out, **kwargs)
try:
if min is None:
return um.minimum(a, max, out=out, **kwargs)
elif max is None:
return um.maximum(a, min, out=out, **kwargs)
else:
return um.clip(a, min, max, out=out, **kwargs)
except OverflowError:
# If there were python ints, an OverflowError due NEP-50 casting
# might be responsible. In that case, we try again.
# (We do not do this up front since this is a rare case, and
# we do not want to slow down the common path.)
if a.dtype.kind not in "iu":
raise

# Circular import if done on top.
from numpy._core.getlimits import iinfo
a_info = iinfo(a.dtype)
changed = False
if isinstance(min, int) and min < a_info.min:
min = a_info.min
changed = True
if isinstance(max, int) and max > a_info.max:
max = a_info.max
changed = True
if not changed:
raise
# try/except just in case the error was unrelated somehow.
try:
return _clip(a, min, max, out=out, **kwargs)
except Exception:
pass
raise

def _mean(a, axis=None, dtype=None, out=None, keepdims=False, *, where=True):
arr = asanyarray(a)
Expand Down
33 changes: 22 additions & 11 deletions numpy/_core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -5015,17 +5015,28 @@ def test_basic(self):
self._clip_type(
'uint', 1024, 10, 100, inplace=inplace)

@pytest.mark.parametrize("inplace", [False, True])
def test_int_range_error(self, inplace):
# E.g. clipping uint with negative integers fails to promote
# (changed with NEP 50 and may be adaptable)
# Similar to last check in `test_basic`
x = (np.random.random(1000) * 255).astype("uint8")
with pytest.raises(OverflowError):
x.clip(-1, 10, out=x if inplace else None)

with pytest.raises(OverflowError):
x.clip(0, 256, out=x if inplace else None)
@pytest.mark.parametrize("do_inplace, min, max, emin, emax", [
(True, -1, 10, 0, 10),
(True, -1, None, 0, 255),
(False, np.int8(-1), None, 0, 255),
(True, 10, 400, 10, 255),
(True, None, 400, 0, 255),
(True, -10, 256, 0, 255),
(False, np.int8(-10), 256, 0, 255),
(False, -10, np.uint16(256), 0, 255),
])
def test_int_out_of_range(self, do_inplace, min, max, emin, emax):
# E.g. clipping uint with negative python integers fails to promote
# for ufunc, but we special-case it in _methods._clip.
# For numpy integers, promotion works, but then the output dtype
# changes, so we cannot do this in-place.
x = np.arange(0, 256, dtype="uint8")
result = x.clip(min, max)
self._check_range(result, emin, emax)
if do_inplace:
result2 = x.clip(min, max, out=x)
assert result2 is x
assert_array_equal(result2, result)

def test_record_array(self):
rec = np.array([(-5, 2.0, 3.0), (5.0, 4.0, 3.0)],
Expand Down