summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--azure-pipelines.yml26
-rw-r--r--doc/release/upcoming_changes/15802.expired.rst9
-rw-r--r--doc/release/upcoming_changes/15804.expired.rst8
-rw-r--r--doc/release/upcoming_changes/15805.expired.rst6
-rw-r--r--doc/source/reference/ufuncs.rst10
-rw-r--r--numpy/core/src/multiarray/dtype_transfer.c77
-rw-r--r--numpy/core/tests/test_multiarray.py11
-rw-r--r--numpy/core/tests/test_regression.py13
-rw-r--r--numpy/f2py/tests/util.py5
-rw-r--r--numpy/lib/function_base.py64
-rw-r--r--numpy/lib/tests/test_function_base.py44
-rw-r--r--numpy/ma/core.py15
-rw-r--r--numpy/ma/tests/test_core.py15
13 files changed, 153 insertions, 150 deletions
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 6b1ff4c28..20a240236 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -218,18 +218,34 @@ stages:
OPENBLAS_SUFFIX: '64_'
steps:
- template: azure-steps-windows.yml
- - job: Linux_PyPy3
+ # - job: Linux_PyPy3
+ # pool:
+ # vmIMage: 'ubuntu-18.04'
+ # steps:
+ # - script: source tools/pypy-test.sh
+ # displayName: 'Run PyPy3 Build / Tests'
+ # - task: PublishTestResults@2
+ # condition: succeededOrFailed()
+ # inputs:
+ # testResultsFiles: '**/test-*.xml'
+ # testRunTitle: 'Publish test results for PyPy3'
+ # failTaskOnFailedTests: true
+ - job: Linux_18_04
pool:
- vmIMage: 'ubuntu-18.04'
+ vmImage: 'ubuntu-18.04'
steps:
- - script: source tools/pypy-test.sh
- displayName: 'Run PyPy3 Build / Tests'
+ - script: |
+ python3 -m pip install --user --upgrade pip setuptools
+ python3 -m pip install --user -r test_requirements.txt
+ CPPFLAGS='' F77=gfortran-5 F90=gfortran-5 \
+ python3 runtests.py --debug-info --mode=full -- -rsx --junitxml=junit/test-results.xml
+ displayName: 'Run Linux 18.04 Build / Tests'
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFiles: '**/test-*.xml'
- testRunTitle: 'Publish test results for PyPy3'
failTaskOnFailedTests: true
+ testRunTitle: 'Publish test results for gcc 4.8'
- job: Linux_gcc48
pool:
vmImage: 'ubuntu-18.04'
diff --git a/doc/release/upcoming_changes/15802.expired.rst b/doc/release/upcoming_changes/15802.expired.rst
new file mode 100644
index 000000000..1a1b373a7
--- /dev/null
+++ b/doc/release/upcoming_changes/15802.expired.rst
@@ -0,0 +1,9 @@
+`numpy.insert` and `numpy.delete` can no longer be passed an axis on 0d arrays
+------------------------------------------------------------------------------
+This concludes a deprecation from 1.9, where when an ``axis`` argument was
+passed to a call to `~numpy.insert` and `~numpy.delete` on a 0d array, the
+``axis`` and ``obj`` argument and indices would be completely ignored.
+In these cases, ``insert(arr, "nonsense", 42, axis=0)`` would actually overwrite the
+entire array, while ``delete(arr, "nonsense", axis=0)`` would be ``arr.copy()``
+
+Now passing ``axis`` on a 0d array raises `~numpy.AxisError`.
diff --git a/doc/release/upcoming_changes/15804.expired.rst b/doc/release/upcoming_changes/15804.expired.rst
new file mode 100644
index 000000000..e110e1ead
--- /dev/null
+++ b/doc/release/upcoming_changes/15804.expired.rst
@@ -0,0 +1,8 @@
+`numpy.delete` no longer ignores out-of-bounds indices
+------------------------------------------------------
+This concludes deprecations from 1.8 and 1.9, where ``np.delete`` would ignore
+both negative and out-of-bounds items in a sequence of indices. This was at
+odds with its behavior when passed a single index.
+
+Now out-of-bounds items throw ``IndexError``, and negative items index from the
+end.
diff --git a/doc/release/upcoming_changes/15805.expired.rst b/doc/release/upcoming_changes/15805.expired.rst
new file mode 100644
index 000000000..d317e98b8
--- /dev/null
+++ b/doc/release/upcoming_changes/15805.expired.rst
@@ -0,0 +1,6 @@
+`numpy.insert` and `numpy.delete` no longer accept non-integral indices
+-----------------------------------------------------------------------
+This concludes a deprecation from 1.9, where sequences of non-integers indices
+were allowed and cast to integers. Now passing sequences of non-integral
+indices raises ``IndexError``, just like it does when passing a single
+non-integral scalar.
diff --git a/doc/source/reference/ufuncs.rst b/doc/source/reference/ufuncs.rst
index 20c89e0b3..aad285122 100644
--- a/doc/source/reference/ufuncs.rst
+++ b/doc/source/reference/ufuncs.rst
@@ -441,13 +441,13 @@ advanced usage and will not typically be used.
*extobj*
- a list of length 1, 2, or 3 specifying the ufunc buffer-size, the
- error mode integer, and the error call-back function. Normally, these
+ a list of length 3 specifying the ufunc buffer-size, the error
+ mode integer, and the error call-back function. Normally, these
values are looked up in a thread-specific dictionary. Passing them
here circumvents that look up and uses the low-level specification
- provided for the error mode. This may be useful, for example, as an
- optimization for calculations requiring many ufunc calls on small arrays
- in a loop.
+ provided for the error mode. This may be useful, for example, as
+ an optimization for calculations requiring many ufunc calls on
+ small arrays in a loop.
diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c
index b26d5ac89..ecaa680ec 100644
--- a/numpy/core/src/multiarray/dtype_transfer.c
+++ b/numpy/core/src/multiarray/dtype_transfer.c
@@ -312,6 +312,7 @@ typedef struct {
NpyAuxData *wrappeddata, *todata, *fromdata;
npy_intp src_itemsize, dst_itemsize;
char *bufferin, *bufferout;
+ npy_bool init_dest, out_needs_api;
} _align_wrap_data;
/* transfer data free function */
@@ -372,6 +373,9 @@ static NpyAuxData *_align_wrap_data_clone(NpyAuxData *data)
}
}
+ newdata->init_dest = d->init_dest;
+ newdata->out_needs_api = d->out_needs_api;
+
return (NpyAuxData *)newdata;
}
@@ -391,57 +395,26 @@ _strided_to_strided_contig_align_wrap(char *dst, npy_intp dst_stride,
*todata = d->todata,
*fromdata = d->fromdata;
char *bufferin = d->bufferin, *bufferout = d->bufferout;
+ npy_bool init_dest = d->init_dest, out_needs_api = d->out_needs_api;
for(;;) {
- if (N > NPY_LOWLEVEL_BUFFER_BLOCKSIZE) {
- tobuffer(bufferin, inner_src_itemsize, src, src_stride,
- NPY_LOWLEVEL_BUFFER_BLOCKSIZE,
- src_itemsize, todata);
- wrapped(bufferout, dst_itemsize, bufferin, inner_src_itemsize,
- NPY_LOWLEVEL_BUFFER_BLOCKSIZE,
- inner_src_itemsize, wrappeddata);
- frombuffer(dst, dst_stride, bufferout, dst_itemsize,
- NPY_LOWLEVEL_BUFFER_BLOCKSIZE,
- dst_itemsize, fromdata);
- N -= NPY_LOWLEVEL_BUFFER_BLOCKSIZE;
- src += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*src_stride;
- dst += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*dst_stride;
- }
- else {
- tobuffer(bufferin, inner_src_itemsize, src, src_stride, N,
- src_itemsize, todata);
- wrapped(bufferout, dst_itemsize, bufferin, inner_src_itemsize, N,
- inner_src_itemsize, wrappeddata);
- frombuffer(dst, dst_stride, bufferout, dst_itemsize, N,
- dst_itemsize, fromdata);
+ /*
+ * The caller does not know if a previous call resulted in a Python
+ * exception. Much of the Python API is unsafe while an exception is in
+ * flight, so just skip all the work. Someone higher in the call stack
+ * will check for errors and propagate them.
+ */
+ if (out_needs_api && PyErr_Occurred()) {
return;
}
- }
-}
-
-static void
-_strided_to_strided_contig_align_wrap_init_dest(char *dst, npy_intp dst_stride,
- char *src, npy_intp src_stride,
- npy_intp N, npy_intp src_itemsize,
- NpyAuxData *data)
-{
- _align_wrap_data *d = (_align_wrap_data *)data;
- PyArray_StridedUnaryOp *wrapped = d->wrapped,
- *tobuffer = d->tobuffer,
- *frombuffer = d->frombuffer;
- npy_intp inner_src_itemsize = d->src_itemsize,
- dst_itemsize = d->dst_itemsize;
- NpyAuxData *wrappeddata = d->wrappeddata,
- *todata = d->todata,
- *fromdata = d->fromdata;
- char *bufferin = d->bufferin, *bufferout = d->bufferout;
-
- for(;;) {
if (N > NPY_LOWLEVEL_BUFFER_BLOCKSIZE) {
tobuffer(bufferin, inner_src_itemsize, src, src_stride,
NPY_LOWLEVEL_BUFFER_BLOCKSIZE,
src_itemsize, todata);
- memset(bufferout, 0, dst_itemsize*NPY_LOWLEVEL_BUFFER_BLOCKSIZE);
+ if (init_dest) {
+ memset(bufferout, 0,
+ dst_itemsize*NPY_LOWLEVEL_BUFFER_BLOCKSIZE);
+ }
wrapped(bufferout, dst_itemsize, bufferin, inner_src_itemsize,
NPY_LOWLEVEL_BUFFER_BLOCKSIZE,
inner_src_itemsize, wrappeddata);
@@ -455,7 +428,9 @@ _strided_to_strided_contig_align_wrap_init_dest(char *dst, npy_intp dst_stride,
else {
tobuffer(bufferin, inner_src_itemsize, src, src_stride, N,
src_itemsize, todata);
- memset(bufferout, 0, dst_itemsize*N);
+ if (init_dest) {
+ memset(bufferout, 0, dst_itemsize*N);
+ }
wrapped(bufferout, dst_itemsize, bufferin, inner_src_itemsize, N,
inner_src_itemsize, wrappeddata);
frombuffer(dst, dst_stride, bufferout, dst_itemsize, N,
@@ -477,6 +452,7 @@ _strided_to_strided_contig_align_wrap_init_dest(char *dst, npy_intp dst_stride,
* wrapped - contig to contig transfer function being wrapped
* wrappeddata - data for wrapped
* init_dest - 1 means to memset the dest buffer to 0 before calling wrapped.
+ * out_needs_api - if NPY_TRUE, check for (and break on) Python API errors.
*
* Returns NPY_SUCCEED or NPY_FAIL.
*/
@@ -487,6 +463,7 @@ wrap_aligned_contig_transfer_function(
PyArray_StridedUnaryOp *frombuffer, NpyAuxData *fromdata,
PyArray_StridedUnaryOp *wrapped, NpyAuxData *wrappeddata,
int init_dest,
+ int out_needs_api,
PyArray_StridedUnaryOp **out_stransfer,
NpyAuxData **out_transferdata)
{
@@ -519,14 +496,11 @@ wrap_aligned_contig_transfer_function(
data->bufferin = (char *)data + basedatasize;
data->bufferout = data->bufferin +
NPY_LOWLEVEL_BUFFER_BLOCKSIZE*src_itemsize;
+ data->init_dest = (npy_bool) init_dest;
+ data->out_needs_api = (npy_bool) out_needs_api;
/* Set the function and data */
- if (init_dest) {
- *out_stransfer = &_strided_to_strided_contig_align_wrap_init_dest;
- }
- else {
- *out_stransfer = &_strided_to_strided_contig_align_wrap;
- }
+ *out_stransfer = &_strided_to_strided_contig_align_wrap;
*out_transferdata = (NpyAuxData *)data;
return NPY_SUCCEED;
@@ -1171,6 +1145,7 @@ get_datetime_to_unicode_transfer_function(int aligned,
frombuffer, fromdata,
caststransfer, castdata,
PyDataType_FLAGCHK(str_dtype, NPY_NEEDS_INIT),
+ *out_needs_api,
out_stransfer, out_transferdata) != NPY_SUCCEED) {
NPY_AUXDATA_FREE(castdata);
NPY_AUXDATA_FREE(todata);
@@ -1293,6 +1268,7 @@ get_unicode_to_datetime_transfer_function(int aligned,
frombuffer, fromdata,
caststransfer, castdata,
PyDataType_FLAGCHK(dst_dtype, NPY_NEEDS_INIT),
+ *out_needs_api,
out_stransfer, out_transferdata) != NPY_SUCCEED) {
Py_DECREF(str_dtype);
NPY_AUXDATA_FREE(castdata);
@@ -1613,6 +1589,7 @@ get_cast_transfer_function(int aligned,
frombuffer, fromdata,
caststransfer, castdata,
PyDataType_FLAGCHK(dst_dtype, NPY_NEEDS_INIT),
+ *out_needs_api,
out_stransfer, out_transferdata) != NPY_SUCCEED) {
NPY_AUXDATA_FREE(castdata);
NPY_AUXDATA_FREE(todata);
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 829679dab..7b3397795 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -16,14 +16,7 @@ from contextlib import contextmanager
from numpy.compat import pickle
-try:
- import pathlib
-except ImportError:
- try:
- import pathlib2 as pathlib
- except ImportError:
- pathlib = None
-
+import pathlib
import builtins
from decimal import Decimal
@@ -4680,14 +4673,12 @@ class TestIO:
y = np.fromfile(self.filename, dtype=self.dtype)
assert_array_equal(y, self.x.flat)
- @pytest.mark.skipif(pathlib is None, reason="pathlib not found")
def test_roundtrip_pathlib(self):
p = pathlib.Path(self.filename)
self.x.tofile(p)
y = np.fromfile(p, dtype=self.dtype)
assert_array_equal(y, self.x.flat)
- @pytest.mark.skipif(pathlib is None, reason="pathlib not found")
def test_roundtrip_dump_pathlib(self):
p = pathlib.Path(self.filename)
self.x.dump(p)
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
index 97898cc20..18d5b6032 100644
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -1408,6 +1408,13 @@ class TestRegression:
dtype='U')
assert_raises(UnicodeEncodeError, np.array, a, 'S4')
+ def test_unicode_to_string_cast_error(self):
+ # gh-15790
+ a = np.array([u'\x80'] * 129, dtype='U3')
+ assert_raises(UnicodeEncodeError, np.array, a, 'S')
+ b = a.reshape(3, 43)[:-1, :-1]
+ assert_raises(UnicodeEncodeError, np.array, b, 'S')
+
def test_mixed_string_unicode_array_creation(self):
a = np.array(['1234', u'123'])
assert_(a.itemsize == 16)
@@ -1501,10 +1508,7 @@ class TestRegression:
test_type(t)
def test_buffer_hashlib(self):
- try:
- from hashlib import md5
- except ImportError:
- from md5 import new as md5
+ from hashlib import md5
x = np.array([1, 2, 3], dtype=np.dtype('<i4'))
assert_equal(md5(x).hexdigest(), '2a1dd1e1e59d0a384c26951e316cd7e6')
@@ -2481,4 +2485,3 @@ class TestRegression:
assert arr.size * arr.itemsize > 2 ** 31
c_arr = np.ctypeslib.as_ctypes(arr)
assert_equal(c_arr._length_, arr.size)
-
diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py
index 6dcc2ed12..c5b06697d 100644
--- a/numpy/f2py/tests/util.py
+++ b/numpy/f2py/tests/util.py
@@ -19,10 +19,7 @@ from numpy.compat import asbytes, asstr
from numpy.testing import temppath
from importlib import import_module
-try:
- from hashlib import md5
-except ImportError:
- from md5 import new as md5 # noqa: F401
+from hashlib import md5
#
# Maintaining a temporary module directory
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index b9f3bbb16..7211b68cf 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -4270,20 +4270,11 @@ def delete(arr, obj, axis=None):
if axis is None:
if ndim != 1:
arr = arr.ravel()
+ # needed for np.matrix, which is still not 1d after being ravelled
ndim = arr.ndim
- axis = -1
-
- if ndim == 0:
- # 2013-09-24, 1.9
- warnings.warn(
- "in the future the special handling of scalars will be removed "
- "from delete and raise an error", DeprecationWarning, stacklevel=3)
- if wrap:
- return wrap(arr)
- else:
- return arr.copy(order=arrorder)
-
- axis = normalize_axis_index(axis, ndim)
+ axis = ndim - 1
+ else:
+ axis = normalize_axis_index(axis, ndim)
slobj = [slice(None)]*ndim
N = arr.shape[axis]
@@ -4344,6 +4335,7 @@ def delete(arr, obj, axis=None):
# After removing the special handling of booleans and out of
# bounds values, the conversion to the array can be removed.
if obj.dtype == bool:
+ # 2012-10-11, NumPy 1.8
warnings.warn("in the future insert will treat boolean arrays and "
"array-likes as boolean index instead of casting it "
"to integer", FutureWarning, stacklevel=3)
@@ -4368,32 +4360,8 @@ def delete(arr, obj, axis=None):
else:
if obj.size == 0 and not isinstance(_obj, np.ndarray):
obj = obj.astype(intp)
- if not np.can_cast(obj, intp, 'same_kind'):
- # obj.size = 1 special case always failed and would just
- # give superfluous warnings.
- # 2013-09-24, 1.9
- warnings.warn(
- "using a non-integer array as obj in delete will result in an "
- "error in the future", DeprecationWarning, stacklevel=3)
- obj = obj.astype(intp)
keep = ones(N, dtype=bool)
- # Test if there are out of bound indices, this is deprecated
- inside_bounds = (obj < N) & (obj >= -N)
- if not inside_bounds.all():
- # 2013-09-24, 1.9
- warnings.warn(
- "in the future out of bounds indices will raise an error "
- "instead of being ignored by `numpy.delete`.",
- DeprecationWarning, stacklevel=3)
- obj = obj[inside_bounds]
- positive_indices = obj >= 0
- if not positive_indices.all():
- warnings.warn(
- "in the future negative indices will not be ignored by "
- "`numpy.delete`.", FutureWarning, stacklevel=3)
- obj = obj[positive_indices]
-
keep[obj, ] = False
slobj[axis] = keep
new = arr[tuple(slobj)]
@@ -4510,19 +4478,9 @@ def insert(arr, obj, values, axis=None):
if axis is None:
if ndim != 1:
arr = arr.ravel()
+ # needed for np.matrix, which is still not 1d after being ravelled
ndim = arr.ndim
axis = ndim - 1
- elif ndim == 0:
- # 2013-09-24, 1.9
- warnings.warn(
- "in the future the special handling of scalars will be removed "
- "from insert and raise an error", DeprecationWarning, stacklevel=3)
- arr = arr.copy(order=arrorder)
- arr[...] = values
- if wrap:
- return wrap(arr)
- else:
- return arr
else:
axis = normalize_axis_index(axis, ndim)
slobj = [slice(None)]*ndim
@@ -4531,12 +4489,13 @@ def insert(arr, obj, values, axis=None):
if isinstance(obj, slice):
# turn it into a range object
- indices = arange(*obj.indices(N), **{'dtype': intp})
+ indices = arange(*obj.indices(N), dtype=intp)
else:
# need to copy obj, because indices will be changed in-place
indices = np.array(obj)
if indices.dtype == bool:
# See also delete
+ # 2012-10-11, NumPy 1.8
warnings.warn(
"in the future insert will treat boolean arrays and "
"array-likes as a boolean index instead of casting it to "
@@ -4586,13 +4545,6 @@ def insert(arr, obj, values, axis=None):
# Can safely cast the empty list to intp
indices = indices.astype(intp)
- if not np.can_cast(indices, intp, 'same_kind'):
- # 2013-09-24, 1.9
- warnings.warn(
- "using a non-integer array as obj in insert will result in an "
- "error in the future", DeprecationWarning, stacklevel=3)
- indices = indices.astype(intp)
-
indices[indices < 0] += N
numnew = len(indices)
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index 860cf452b..fb10205d4 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -509,12 +509,11 @@ class TestInsert:
insert(a, 1, a[:, 2, :], axis=1))
def test_0d(self):
- # This is an error in the future
a = np.array(1)
- with warnings.catch_warnings(record=True) as w:
- warnings.filterwarnings('always', '', DeprecationWarning)
- assert_equal(insert(a, [], 2, axis=0), np.array(2))
- assert_(w[0].category is DeprecationWarning)
+ with pytest.raises(np.AxisError):
+ insert(a, [], 2, axis=0)
+ with pytest.raises(TypeError):
+ insert(a, [], 2, axis="nonsense")
def test_subclass(self):
class SubClass(np.ndarray):
@@ -544,6 +543,12 @@ class TestInsert:
b = np.insert(a, [0, 2], val)
assert_array_equal(b[[0, 3]], np.array(val, dtype=b.dtype))
+ def test_index_floats(self):
+ with pytest.raises(IndexError):
+ np.insert([0, 1, 2], np.array([1.0, 2.0]), [10, 20])
+ with pytest.raises(IndexError):
+ np.insert([0, 1, 2], np.array([], dtype=float), [])
+
class TestAmax:
@@ -807,7 +812,6 @@ class TestDelete:
# NOTE: The cast should be removed after warning phase for bools
if not isinstance(indices, (slice, int, long, np.integer)):
indices = np.asarray(indices, dtype=np.intp)
- indices = indices[(indices >= 0) & (indices < 5)]
assert_array_equal(setxor1d(a_del, self.a[indices, ]), self.a,
err_msg=msg)
xor = setxor1d(nd_a_del[0,:, 0], self.nd_a[0, indices, 0])
@@ -825,15 +829,19 @@ class TestDelete:
def test_fancy(self):
# Deprecation/FutureWarning tests should be kept after change.
self._check_inverse_of_slicing(np.array([[0, 1], [2, 1]]))
- with warnings.catch_warnings():
- warnings.filterwarnings('error', category=DeprecationWarning)
- assert_raises(DeprecationWarning, delete, self.a, [100])
- assert_raises(DeprecationWarning, delete, self.a, [-100])
+ with pytest.raises(IndexError):
+ delete(self.a, [100])
+ with pytest.raises(IndexError):
+ delete(self.a, [-100])
+
+ self._check_inverse_of_slicing([0, -1, 2, 2])
+
with warnings.catch_warnings(record=True) as w:
warnings.filterwarnings('always', category=FutureWarning)
- self._check_inverse_of_slicing([0, -1, 2, 2])
obj = np.array([True, False, False], dtype=bool)
self._check_inverse_of_slicing(obj)
+ # _check_inverse_of_slicing operates on two arrays, so warns twice
+ assert len(w) == 2
assert_(w[0].category is FutureWarning)
assert_(w[1].category is FutureWarning)
@@ -843,10 +851,10 @@ class TestDelete:
def test_0d(self):
a = np.array(1)
- with warnings.catch_warnings(record=True) as w:
- warnings.filterwarnings('always', '', DeprecationWarning)
- assert_equal(delete(a, [], axis=0), a)
- assert_(w[0].category is DeprecationWarning)
+ with pytest.raises(np.AxisError):
+ delete(a, [], axis=0)
+ with pytest.raises(TypeError):
+ delete(a, [], axis="nonsense")
def test_subclass(self):
class SubClass(np.ndarray):
@@ -868,6 +876,12 @@ class TestDelete:
assert_equal(m.flags.c_contiguous, k.flags.c_contiguous)
assert_equal(m.flags.f_contiguous, k.flags.f_contiguous)
+ def test_index_floats(self):
+ with pytest.raises(IndexError):
+ np.delete([0, 1, 2], np.array([1.0, 2.0]))
+ with pytest.raises(IndexError):
+ np.delete([0, 1, 2], np.array([], dtype=float))
+
class TestGradient:
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index e24cb956c..5c446252b 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -6385,6 +6385,21 @@ class MaskedConstant(MaskedArray):
# it's a subclass, or something is wrong, make it obvious
return object.__repr__(self)
+ def __format__(self, format_spec):
+ # Replace ndarray.__format__ with the default, which supports no format characters.
+ # Supporting format characters is unwise here, because we do not know what type
+ # the user was expecting - better to not guess.
+ try:
+ return object.__format__(self, format_spec)
+ except TypeError:
+ # 2020-03-23, NumPy 1.19.0
+ warnings.warn(
+ "Format strings passed to MaskedConstant are ignored, but in future may "
+ "error or produce different behavior",
+ FutureWarning, stacklevel=2
+ )
+ return object.__format__(self, "")
+
def __reduce__(self):
"""Override of MaskedArray's __reduce__.
"""
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
index 3bfb42555..98fc7dd97 100644
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -447,6 +447,21 @@ class TestMaskedArray:
assert_equal(copied.mask, [0, 0, 0])
assert_equal(a.mask, [0, 1, 0])
+ def test_format(self):
+ a = array([0, 1, 2], mask=[False, True, False])
+ assert_equal(format(a), "[0 -- 2]")
+ assert_equal(format(masked), "--")
+ assert_equal(format(masked, ""), "--")
+
+ # Postponed from PR #15410, perhaps address in the future.
+ # assert_equal(format(masked, " >5"), " --")
+ # assert_equal(format(masked, " <5"), "-- ")
+
+ # Expect a FutureWarning for using format_spec with MaskedElement
+ with assert_warns(FutureWarning):
+ with_format_string = format(masked, " >5")
+ assert_equal(with_format_string, "--")
+
def test_str_repr(self):
a = array([0, 1, 2], mask=[False, True, False])
assert_equal(str(a), '[0 -- 2]')