diff options
author | Charles Harris <charlesr.harris@gmail.com> | 2015-09-10 12:02:38 -0600 |
---|---|---|
committer | Charles Harris <charlesr.harris@gmail.com> | 2015-09-10 12:02:38 -0600 |
commit | 0b0206c1617841ed2e5324de752ee8ede2cce791 (patch) | |
tree | e06fbf8d42e288be9ca5f205884acc0381f867ee | |
parent | 438cdd3d75a0bb606e7ab7f96e59744c9b78d748 (diff) | |
parent | dbe5cef95dcaa3c48dad1093084b7d2b65cf889b (diff) | |
download | numpy-0b0206c1617841ed2e5324de752ee8ede2cce791.tar.gz |
Merge pull request #6271 from charris/change-deprecated-indexes-to-error
DEP,MAINT: Change deprecated indexing to errors.
-rw-r--r-- | doc/release/1.11.0-notes.rst | 14 | ||||
-rw-r--r-- | numpy/core/src/multiarray/conversion_utils.c | 94 | ||||
-rw-r--r-- | numpy/core/src/multiarray/mapping.c | 126 | ||||
-rw-r--r-- | numpy/core/src/multiarray/number.c | 13 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 231 | ||||
-rw-r--r-- | numpy/core/tests/test_indexing.py | 243 | ||||
-rw-r--r-- | numpy/lib/tests/test_function_base.py | 18 |
7 files changed, 259 insertions, 480 deletions
diff --git a/doc/release/1.11.0-notes.rst b/doc/release/1.11.0-notes.rst index 33bc24e30..9afe6e866 100644 --- a/doc/release/1.11.0-notes.rst +++ b/doc/release/1.11.0-notes.rst @@ -20,6 +20,20 @@ Future Changes: Compatibility notes =================== +Deprecated to error +~~~~~~~~~~~~~~~~~~~ + +* Indexing with floats raises IndexError, + e.g., a[0, 0.0]. +* Indexing with non-integer array_like raises IndexError, + e.g., a['1', '2'] +* Indexing with multiple ellipsis raises IndexError, + e.g., a[..., ...]. +* Indexing with boolean where integer expected raises IndexError, + e.g., a[False:True:True]. +* Non-integers used as index values raise TypeError, + e.g., in reshape, take, and specifying reduce axis. + New Features ============ diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index 3b9a10da5..88064c1d6 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -777,20 +777,16 @@ PyArray_PyIntAsIntp_ErrMsg(PyObject *o, const char * msg) #endif PyObject *obj, *err; - if (!o) { + /* + * Be a bit stricter and not allow bools. + * np.bool_ is also disallowed as Boolean arrays do not currently + * support index. + */ + if (!o || PyBool_Check(o) || PyArray_IsScalar(o, Bool)) { PyErr_SetString(PyExc_TypeError, msg); return -1; } - /* Be a bit stricter and not allow bools, np.bool_ is handled later */ - if (PyBool_Check(o)) { - /* 2013-04-13, 1.8 */ - if (DEPRECATE("using a boolean instead of an integer" - " will result in an error in the future") < 0) { - return -1; - } - } - /* * Since it is the usual case, first check if o is an integer. This is * an exact check, since otherwise __index__ is used. @@ -816,84 +812,22 @@ PyArray_PyIntAsIntp_ErrMsg(PyObject *o, const char * msg) return (npy_intp)long_value; } - /* Disallow numpy.bool_. Boolean arrays do not currently support index. */ - if (PyArray_IsScalar(o, Bool)) { - /* 2013-06-09, 1.8 */ - if (DEPRECATE("using a boolean instead of an integer" - " will result in an error in the future") < 0) { - return -1; - } - } - /* * The most general case. PyNumber_Index(o) covers everything * including arrays. In principle it may be possible to replace * the whole function by PyIndex_AsSSize_t after deprecation. */ obj = PyNumber_Index(o); - if (obj) { + if (obj == NULL) { + return -1; + } #if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) - long_value = PyLong_AsLongLong(obj); + long_value = PyLong_AsLongLong(obj); #else - long_value = PyLong_AsLong(obj); + long_value = PyLong_AsLong(obj); #endif - Py_DECREF(obj); - goto finish; - } - else { - /* - * Set the TypeError like PyNumber_Index(o) would after trying - * the general case. - */ - PyErr_Clear(); - } + Py_DECREF(obj); - /* - * For backward compatibility check the number C-Api number protcol - * This should be removed up the finish label after deprecation. - */ - if (Py_TYPE(o)->tp_as_number != NULL && - Py_TYPE(o)->tp_as_number->nb_int != NULL) { - obj = Py_TYPE(o)->tp_as_number->nb_int(o); - if (obj == NULL) { - return -1; - } - #if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) - long_value = PyLong_AsLongLong(obj); - #else - long_value = PyLong_AsLong(obj); - #endif - Py_DECREF(obj); - } -#if !defined(NPY_PY3K) - else if (Py_TYPE(o)->tp_as_number != NULL && - Py_TYPE(o)->tp_as_number->nb_long != NULL) { - obj = Py_TYPE(o)->tp_as_number->nb_long(o); - if (obj == NULL) { - return -1; - } - #if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) - long_value = PyLong_AsLongLong(obj); - #else - long_value = PyLong_AsLong(obj); - #endif - Py_DECREF(obj); - } -#endif - else { - PyErr_SetString(PyExc_TypeError, msg); - return -1; - } - /* Give a deprecation warning, unless there was already an error */ - if (!error_converting(long_value)) { - /* 2013-04-13, 1.8 */ - if (DEPRECATE("using a non-integer number instead of an integer" - " will result in an error in the future") < 0) { - return -1; - } - } - - finish: if (error_converting(long_value)) { err = PyErr_Occurred(); /* Only replace TypeError's here, which are the normal errors. */ @@ -902,9 +836,9 @@ PyArray_PyIntAsIntp_ErrMsg(PyObject *o, const char * msg) } return -1; } - goto overflow_check; /* silence unused warning */ - overflow_check: + +overflow_check: #if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) #if (NPY_SIZEOF_LONGLONG > NPY_SIZEOF_INTP) if ((long_value < NPY_MIN_INTP) || (long_value > NPY_MAX_INTP)) { diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c index b6e831498..42a12db14 100644 --- a/numpy/core/src/multiarray/mapping.c +++ b/numpy/core/src/multiarray/mapping.c @@ -285,35 +285,13 @@ prepare_index(PyArrayObject *self, PyObject *index, /* Index is an ellipsis (`...`) */ if (obj == Py_Ellipsis) { - /* - * If there is more then one Ellipsis, it is replaced. Deprecated, - * since it is hard to imagine anyone using two Ellipsis and - * actually planning on all but the first being automatically - * replaced with a slice. - */ + /* At most one ellipsis in an index */ if (index_type & HAS_ELLIPSIS) { - /* 2013-04-14, 1.8 */ - if (DEPRECATE( - "an index can only have a single Ellipsis (`...`); " - "replace all but one with slices (`:`).") < 0) { - goto failed_building_indices; - } - index_type |= HAS_SLICE; - - indices[curr_idx].type = HAS_SLICE; - indices[curr_idx].object = PySlice_New(NULL, NULL, NULL); - - if (indices[curr_idx].object == NULL) { - goto failed_building_indices; - } - - used_ndim += 1; - new_ndim += 1; - curr_idx += 1; - continue; + PyErr_Format(PyExc_IndexError, + "an index can only have a single ellipsis ('...')"); + goto failed_building_indices; } index_type |= HAS_ELLIPSIS; - indices[curr_idx].type = HAS_ELLIPSIS; indices[curr_idx].object = NULL; /* number of slices it is worth, won't update if it is 0: */ @@ -415,102 +393,8 @@ prepare_index(PyArrayObject *self, PyObject *index, goto failed_building_indices; } } - /* - * Special case to allow 0-d boolean indexing with - * scalars. Should be removed after boolean-array - * like as integer-array like deprecation. - * (does not cover ufunc.at, because it does not use the - * boolean special case, but that should not matter...) - * Since all but strictly boolean indices are invalid, - * there is no need for any further conversion tries. - */ - else if (PyArray_NDIM(self) == 0) { - arr = tmp_arr; - } else { - /* - * These Checks can be removed after deprecation, since - * they should then be either correct already or error out - * later just like a normal array. - */ - if (PyArray_ISBOOL(tmp_arr)) { - /* 2013-04-14, 1.8 */ - if (DEPRECATE_FUTUREWARNING( - "in the future, boolean array-likes will be " - "handled as a boolean array index") < 0) { - Py_DECREF(tmp_arr); - goto failed_building_indices; - } - if (PyArray_NDIM(tmp_arr) == 0) { - /* - * Need to raise an error here, since the - * DeprecationWarning before was not triggered. - * TODO: A `False` triggers a Deprecation *not* a - * a FutureWarning. - */ - PyErr_SetString(PyExc_IndexError, - "in the future, 0-d boolean arrays will be " - "interpreted as a valid boolean index"); - Py_DECREF(tmp_arr); - goto failed_building_indices; - } - else { - arr = tmp_arr; - } - } - /* - * Note: Down the road, the integers will be cast to intp. - * The user has to make sure they can be safely cast. - * If not, we might index wrong instead of an giving - * an error. - */ - else if (!PyArray_ISINTEGER(tmp_arr)) { - if (PyArray_NDIM(tmp_arr) == 0) { - /* match integer deprecation warning */ - /* 2013-09-25, 1.8 */ - if (DEPRECATE( - "using a non-integer number instead of an " - "integer will result in an error in the " - "future") < 0) { - - /* The error message raised in the future */ - PyErr_SetString(PyExc_IndexError, - "only integers, slices (`:`), ellipsis (`...`), " - "numpy.newaxis (`None`) and integer or boolean " - "arrays are valid indices"); - Py_DECREF((PyObject *)tmp_arr); - goto failed_building_indices; - } - } - else { - /* 2013-09-25, 1.8 */ - if (DEPRECATE( - "non integer (and non boolean) array-likes " - "will not be accepted as indices in the " - "future") < 0) { - - /* Error message to be raised in the future */ - PyErr_SetString(PyExc_IndexError, - "non integer (and non boolean) array-likes will " - "not be accepted as indices in the future"); - Py_DECREF((PyObject *)tmp_arr); - goto failed_building_indices; - } - } - } - - arr = (PyArrayObject *)PyArray_FromArray(tmp_arr, - PyArray_DescrFromType(NPY_INTP), - NPY_ARRAY_FORCECAST); - - if (arr == NULL) { - /* Since this will be removed, handle this later */ - PyErr_Clear(); - arr = tmp_arr; - } - else { - Py_DECREF((PyObject *)tmp_arr); - } + arr = tmp_arr; } } else { diff --git a/numpy/core/src/multiarray/number.c b/numpy/core/src/multiarray/number.c index 953a84eef..fec015a30 100644 --- a/numpy/core/src/multiarray/number.c +++ b/numpy/core/src/multiarray/number.c @@ -1025,18 +1025,11 @@ _array_copy_nice(PyArrayObject *self) static PyObject * array_index(PyArrayObject *v) { - if (!PyArray_ISINTEGER(v) || PyArray_SIZE(v) != 1) { - PyErr_SetString(PyExc_TypeError, "only integer arrays with " \ - "one element can be converted to an index"); + if (!PyArray_ISINTEGER(v) || PyArray_NDIM(v) != 0) { + PyErr_SetString(PyExc_TypeError, + "only integer scalar arrays can be converted to a scalar index"); return NULL; } - if (PyArray_NDIM(v) != 0) { - /* 2013-04-20, 1.8 */ - if (DEPRECATE("converting an array with ndim > 0 to an index" - " will result in an error in the future") < 0) { - return NULL; - } - } return PyArray_DESCR(v)->f->getitem(PyArray_DATA(v), v); } diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 8ec0f5e7f..3e76409c5 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -115,237 +115,6 @@ class _DeprecationTestCase(object): exceptions=tuple(), args=args, kwargs=kwargs) -class TestFloatNonIntegerArgumentDeprecation(_DeprecationTestCase): - """ - These test that ``DeprecationWarning`` is given when you try to use - non-integers as arguments to for indexing and slicing e.g. ``a[0.0:5]`` - and ``a[0.5]``, or other functions like ``array.reshape(1., -1)``. - - After deprecation, changes need to be done inside conversion_utils.c - in PyArray_PyIntAsIntp and possibly PyArray_IntpConverter. - In iterators.c the function slice_GetIndices could be removed in favor - of its python equivalent and in mapping.c the function _tuple_of_integers - can be simplified (if ``np.array([1]).__index__()`` is also deprecated). - - As for the deprecation time-frame: via Ralf Gommers, - - "Hard to put that as a version number, since we don't know if the - version after 1.8 will be 6 months or 2 years after. I'd say 2 - years is reasonable." - - I interpret this to mean 2 years after the 1.8 release. Possibly - giving a PendingDeprecationWarning before that (which is visible - by default) - - """ - message = "using a non-integer number instead of an integer " \ - "will result in an error in the future" - - def test_indexing(self): - a = np.array([[[5]]]) - - def assert_deprecated(*args, **kwargs): - self.assert_deprecated(*args, exceptions=(IndexError,), **kwargs) - - assert_deprecated(lambda: a[0.0]) - assert_deprecated(lambda: a[0, 0.0]) - assert_deprecated(lambda: a[0.0, 0]) - assert_deprecated(lambda: a[0.0,:]) - assert_deprecated(lambda: a[:, 0.0]) - assert_deprecated(lambda: a[:, 0.0,:]) - assert_deprecated(lambda: a[0.0,:,:]) - assert_deprecated(lambda: a[0, 0, 0.0]) - assert_deprecated(lambda: a[0.0, 0, 0]) - assert_deprecated(lambda: a[0, 0.0, 0]) - assert_deprecated(lambda: a[-1.4]) - assert_deprecated(lambda: a[0, -1.4]) - assert_deprecated(lambda: a[-1.4, 0]) - assert_deprecated(lambda: a[-1.4,:]) - assert_deprecated(lambda: a[:, -1.4]) - assert_deprecated(lambda: a[:, -1.4,:]) - assert_deprecated(lambda: a[-1.4,:,:]) - assert_deprecated(lambda: a[0, 0, -1.4]) - assert_deprecated(lambda: a[-1.4, 0, 0]) - assert_deprecated(lambda: a[0, -1.4, 0]) - - # Test that the slice parameter deprecation warning doesn't mask - # the scalar index warning. - assert_deprecated(lambda: a[0.0:, 0.0], num=2) - assert_deprecated(lambda: a[0.0:, 0.0,:], num=2) - - def test_valid_indexing(self): - a = np.array([[[5]]]) - assert_not_deprecated = self.assert_not_deprecated - - assert_not_deprecated(lambda: a[np.array([0])]) - assert_not_deprecated(lambda: a[[0, 0]]) - assert_not_deprecated(lambda: a[:, [0, 0]]) - assert_not_deprecated(lambda: a[:, 0,:]) - assert_not_deprecated(lambda: a[:,:,:]) - - def test_slicing(self): - a = np.array([[5]]) - - def assert_deprecated(*args, **kwargs): - self.assert_deprecated(*args, exceptions=(IndexError,), **kwargs) - - # start as float. - assert_deprecated(lambda: a[0.0:]) - assert_deprecated(lambda: a[0:, 0.0:2]) - assert_deprecated(lambda: a[0.0::2, :0]) - assert_deprecated(lambda: a[0.0:1:2,:]) - assert_deprecated(lambda: a[:, 0.0:]) - # stop as float. - assert_deprecated(lambda: a[:0.0]) - assert_deprecated(lambda: a[:0, 1:2.0]) - assert_deprecated(lambda: a[:0.0:2, :0]) - assert_deprecated(lambda: a[:0.0,:]) - assert_deprecated(lambda: a[:, 0:4.0:2]) - # step as float. - assert_deprecated(lambda: a[::1.0]) - assert_deprecated(lambda: a[0:, :2:2.0]) - assert_deprecated(lambda: a[1::4.0, :0]) - assert_deprecated(lambda: a[::5.0,:]) - assert_deprecated(lambda: a[:, 0:4:2.0]) - # mixed. - assert_deprecated(lambda: a[1.0:2:2.0], num=2) - assert_deprecated(lambda: a[1.0::2.0], num=2) - assert_deprecated(lambda: a[0:, :2.0:2.0], num=2) - assert_deprecated(lambda: a[1.0:1:4.0, :0], num=2) - assert_deprecated(lambda: a[1.0:5.0:5.0,:], num=3) - assert_deprecated(lambda: a[:, 0.4:4.0:2.0], num=3) - # should still get the DeprecationWarning if step = 0. - assert_deprecated(lambda: a[::0.0], function_fails=True) - - def test_valid_slicing(self): - a = np.array([[[5]]]) - assert_not_deprecated = self.assert_not_deprecated - - assert_not_deprecated(lambda: a[::]) - assert_not_deprecated(lambda: a[0:]) - assert_not_deprecated(lambda: a[:2]) - assert_not_deprecated(lambda: a[0:2]) - assert_not_deprecated(lambda: a[::2]) - assert_not_deprecated(lambda: a[1::2]) - assert_not_deprecated(lambda: a[:2:2]) - assert_not_deprecated(lambda: a[1:2:2]) - - def test_non_integer_argument_deprecations(self): - a = np.array([[5]]) - - self.assert_deprecated(np.reshape, args=(a, (1., 1., -1)), num=2) - self.assert_deprecated(np.reshape, args=(a, (np.array(1.), -1))) - self.assert_deprecated(np.take, args=(a, [0], 1.)) - self.assert_deprecated(np.take, args=(a, [0], np.float64(1.))) - - def test_non_integer_sequence_multiplication(self): - # Numpy scalar sequence multiply should not work with non-integers - def mult(a, b): - return a * b - - self.assert_deprecated(mult, args=([1], np.float_(3))) - self.assert_not_deprecated(mult, args=([1], np.int_(3))) - - def test_reduce_axis_float_index(self): - d = np.zeros((3,3,3)) - self.assert_deprecated(np.min, args=(d, 0.5)) - self.assert_deprecated(np.min, num=1, args=(d, (0.5, 1))) - self.assert_deprecated(np.min, num=1, args=(d, (1, 2.2))) - self.assert_deprecated(np.min, num=2, args=(d, (.2, 1.2))) - - -class TestBooleanArgumentDeprecation(_DeprecationTestCase): - """This tests that using a boolean as integer argument/indexing is - deprecated. - - This should be kept in sync with TestFloatNonIntegerArgumentDeprecation - and like it is handled in PyArray_PyIntAsIntp. - """ - message = "using a boolean instead of an integer " \ - "will result in an error in the future" - - def test_bool_as_int_argument(self): - a = np.array([[[1]]]) - - self.assert_deprecated(np.reshape, args=(a, (True, -1))) - self.assert_deprecated(np.reshape, args=(a, (np.bool_(True), -1))) - # Note that operator.index(np.array(True)) does not work, a boolean - # array is thus also deprecated, but not with the same message: - assert_raises(TypeError, operator.index, np.array(True)) - self.assert_deprecated(np.take, args=(a, [0], False)) - self.assert_deprecated(lambda: a[False:True:True], exceptions=IndexError, num=3) - self.assert_deprecated(lambda: a[False, 0], exceptions=IndexError) - self.assert_deprecated(lambda: a[False, 0, 0], exceptions=IndexError) - - -class TestArrayToIndexDeprecation(_DeprecationTestCase): - """This tests that creating an an index from an array is deprecated - if the array is not 0d. - - This can probably be deprecated somewhat faster then the integer - deprecations. The deprecation period started with NumPy 1.8. - For deprecation this needs changing of array_index in number.c - """ - message = "converting an array with ndim \> 0 to an index will result " \ - "in an error in the future" - - def test_array_to_index_deprecation(self): - # This drops into the non-integer deprecation, which is ignored here, - # so no exception is expected. The raising is effectively tested above. - a = np.array([[[1]]]) - - self.assert_deprecated(operator.index, args=(np.array([1]),)) - self.assert_deprecated(np.reshape, args=(a, (a, -1)), exceptions=()) - self.assert_deprecated(np.take, args=(a, [0], a), exceptions=()) - # Check slicing. Normal indexing checks arrays specifically. - self.assert_deprecated(lambda: a[a:a:a], exceptions=(), num=3) - -class TestNonIntegerArrayLike(_DeprecationTestCase): - """Tests that array likes, i.e. lists give a deprecation warning - when they cannot be safely cast to an integer. - """ - message = "non integer \(and non boolean\) array-likes will not be " \ - "accepted as indices in the future" - - def test_basic(self): - a = np.arange(10) - self.assert_deprecated(a.__getitem__, args=([0.5, 1.5],), - exceptions=IndexError) - self.assert_deprecated(a.__getitem__, args=((['1', '2'],),), - exceptions=IndexError) - - self.assert_not_deprecated(a.__getitem__, ([],)) - - def test_boolean_futurewarning(self): - a = np.arange(10) - with warnings.catch_warnings(): - warnings.filterwarnings('always') - assert_warns(FutureWarning, a.__getitem__, [True]) - # Unfortunatly, the deprecation warning takes precedence: - #assert_warns(FutureWarning, a.__getitem__, True) - - with warnings.catch_warnings(): - warnings.filterwarnings('error') - assert_raises(FutureWarning, a.__getitem__, [True]) - #assert_raises(FutureWarning, a.__getitem__, True) - - -class TestMultipleEllipsisDeprecation(_DeprecationTestCase): - message = "an index can only have a single Ellipsis \(`...`\); replace " \ - "all but one with slices \(`:`\)." - - def test_basic(self): - a = np.arange(10) - self.assert_deprecated(a.__getitem__, args=((Ellipsis, Ellipsis),)) - - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', '', DeprecationWarning) - # Just check that this works: - b = a[...,...] - assert_array_equal(a, b) - assert_raises(IndexError, a.__getitem__, ((Ellipsis, ) * 3,)) - - class TestBooleanUnaryMinusDeprecation(_DeprecationTestCase): """Test deprecation of unary boolean `-`. While + and * are well defined, unary - is not and even a corrected form seems to have diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py index 4bc937e0b..38280d05e 100644 --- a/numpy/core/tests/test_indexing.py +++ b/numpy/core/tests/test_indexing.py @@ -3,6 +3,7 @@ from __future__ import division, absolute_import, print_function import sys import warnings import functools +import operator import numpy as np from numpy.core.multiarray_tests import array_indexing @@ -21,6 +22,69 @@ except ImportError: class TestIndexing(TestCase): + def test_index_no_floats(self): + a = np.array([[[5]]]) + + assert_raises(IndexError, lambda: a[0.0]) + assert_raises(IndexError, lambda: a[0, 0.0]) + assert_raises(IndexError, lambda: a[0.0, 0]) + assert_raises(IndexError, lambda: a[0.0,:]) + assert_raises(IndexError, lambda: a[:, 0.0]) + assert_raises(IndexError, lambda: a[:, 0.0,:]) + assert_raises(IndexError, lambda: a[0.0,:,:]) + assert_raises(IndexError, lambda: a[0, 0, 0.0]) + assert_raises(IndexError, lambda: a[0.0, 0, 0]) + assert_raises(IndexError, lambda: a[0, 0.0, 0]) + assert_raises(IndexError, lambda: a[-1.4]) + assert_raises(IndexError, lambda: a[0, -1.4]) + assert_raises(IndexError, lambda: a[-1.4, 0]) + assert_raises(IndexError, lambda: a[-1.4,:]) + assert_raises(IndexError, lambda: a[:, -1.4]) + assert_raises(IndexError, lambda: a[:, -1.4,:]) + assert_raises(IndexError, lambda: a[-1.4,:,:]) + assert_raises(IndexError, lambda: a[0, 0, -1.4]) + assert_raises(IndexError, lambda: a[-1.4, 0, 0]) + assert_raises(IndexError, lambda: a[0, -1.4, 0]) + assert_raises(IndexError, lambda: a[0.0:, 0.0]) + assert_raises(IndexError, lambda: a[0.0:, 0.0,:]) + + def test_slicing_no_floats(self): + a = np.array([[5]]) + + # start as float. + assert_raises(IndexError, lambda: a[0.0:]) + assert_raises(IndexError, lambda: a[0:, 0.0:2]) + assert_raises(IndexError, lambda: a[0.0::2, :0]) + assert_raises(IndexError, lambda: a[0.0:1:2,:]) + assert_raises(IndexError, lambda: a[:, 0.0:]) + # stop as float. + assert_raises(IndexError, lambda: a[:0.0]) + assert_raises(IndexError, lambda: a[:0, 1:2.0]) + assert_raises(IndexError, lambda: a[:0.0:2, :0]) + assert_raises(IndexError, lambda: a[:0.0,:]) + assert_raises(IndexError, lambda: a[:, 0:4.0:2]) + # step as float. + assert_raises(IndexError, lambda: a[::1.0]) + assert_raises(IndexError, lambda: a[0:, :2:2.0]) + assert_raises(IndexError, lambda: a[1::4.0, :0]) + assert_raises(IndexError, lambda: a[::5.0,:]) + assert_raises(IndexError, lambda: a[:, 0:4:2.0]) + # mixed. + assert_raises(IndexError, lambda: a[1.0:2:2.0]) + assert_raises(IndexError, lambda: a[1.0::2.0]) + assert_raises(IndexError, lambda: a[0:, :2.0:2.0]) + assert_raises(IndexError, lambda: a[1.0:1:4.0, :0]) + assert_raises(IndexError, lambda: a[1.0:5.0:5.0,:]) + assert_raises(IndexError, lambda: a[:, 0.4:4.0:2.0]) + # should still get the DeprecationWarning if step = 0. + assert_raises(IndexError, lambda: a[::0.0]) + + def test_index_no_array_to_index(self): + # No non-scalar arrays. + a = np.array([[[1]]]) + + assert_raises(IndexError, lambda: a[a:a:a]) + def test_none_index(self): # `None` index adds newaxis a = np.array([1, 2, 3]) @@ -35,19 +99,9 @@ class TestIndexing(TestCase): a = np.array(0) assert_(isinstance(a[()], np.int_)) - # Regression, it needs to fall through integer and fancy indexing - # cases, so need the with statement to ignore the non-integer error. - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', '', DeprecationWarning) - a = np.array([1.]) - assert_(isinstance(a[0.], np.float_)) - - a = np.array([np.array(1)], dtype=object) - assert_(isinstance(a[0.], np.ndarray)) - def test_same_kind_index_casting(self): - # Indexes should be cast with same-kind and not safe, even if - # that is somewhat unsafe. So test various different code paths. + # Indexes should be cast with same-kind and not safe, even if that + # is somewhat unsafe. So test various different code paths. index = np.arange(5) u_index = index.astype(np.uintp) arr = np.arange(10) @@ -85,7 +139,8 @@ class TestIndexing(TestCase): [4, 5, 6], [7, 8, 9]]) assert_equal(a[...], a) - assert_(a[...].base is a) # `a[...]` was `a` in numpy <1.9.) + # `a[...]` was `a` in numpy <1.9. + assert_(a[...].base is a) # Slicing with ellipsis can skip an # arbitrary number of dimensions @@ -645,7 +700,8 @@ class TestMultiIndexingAutomated(TestCase): np.zeros([1]*31, dtype=int), # trigger too large array. np.array([0., 1.])] # invalid datatype # Some simpler indices that still cover a bit more - self.simple_indices = [Ellipsis, None, -1, [1], np.array([True]), 'skip'] + self.simple_indices = [Ellipsis, None, -1, [1], np.array([True]), + 'skip'] # Very simple ones to fill the rest: self.fill_indices = [slice(None, None), 0] @@ -719,16 +775,18 @@ class TestMultiIndexingAutomated(TestCase): indx = np.array(indx, dtype=np.intp) in_indices[i] = indx elif indx.dtype.kind != 'b' and indx.dtype.kind != 'i': - raise IndexError('arrays used as indices must be of integer (or boolean) type') + raise IndexError('arrays used as indices must be of ' + 'integer (or boolean) type') if indx.ndim != 0: no_copy = False ndim += 1 fancy_dim += 1 if arr.ndim - ndim < 0: - # we can't take more dimensions then we have, not even for 0-d arrays. - # since a[()] makes sense, but not a[(),]. We will raise an error - # later on, unless a broadcasting error occurs first. + # we can't take more dimensions then we have, not even for 0-d + # arrays. since a[()] makes sense, but not a[(),]. We will + # raise an error later on, unless a broadcasting error occurs + # first. raise IndexError if ndim == 0 and None not in in_indices: @@ -736,7 +794,8 @@ class TestMultiIndexingAutomated(TestCase): return arr.copy(), no_copy if ellipsis_pos is not None: - in_indices[ellipsis_pos:ellipsis_pos+1] = [slice(None, None)] * (arr.ndim - ndim) + in_indices[ellipsis_pos:ellipsis_pos+1] = ([slice(None, None)] * + (arr.ndim - ndim)) for ax, indx in enumerate(in_indices): if isinstance(indx, slice): @@ -779,21 +838,23 @@ class TestMultiIndexingAutomated(TestCase): if indx >= arr.shape[ax] or indx < -arr.shape[ax]: raise IndexError if indx.ndim == 0: - # The index is a scalar. This used to be two fold, but if fancy - # indexing was active, the check was done later, possibly - # after broadcasting it away (1.7. or earlier). Now it is always - # done. + # The index is a scalar. This used to be two fold, but if + # fancy indexing was active, the check was done later, + # possibly after broadcasting it away (1.7. or earlier). + # Now it is always done. if indx >= arr.shape[ax] or indx < - arr.shape[ax]: raise IndexError - if len(indices) > 0 and indices[-1][0] == 'f' and ax != ellipsis_pos: + if (len(indices) > 0 and + indices[-1][0] == 'f' and + ax != ellipsis_pos): # NOTE: There could still have been a 0-sized Ellipsis # between them. Checked that with ellipsis_pos. indices[-1].append(indx) else: # We have a fancy index that is not after an existing one. - # NOTE: A 0-d array triggers this as well, while - # one may expect it to not trigger it, since a scalar - # would not be considered fancy indexing. + # NOTE: A 0-d array triggers this as well, while one may + # expect it to not trigger it, since a scalar would not be + # considered fancy indexing. num_fancy += 1 indices.append(['f', indx]) @@ -854,13 +915,15 @@ class TestMultiIndexingAutomated(TestCase): # Work around for a crash or IndexError with 'wrap' # in some 0-sized cases. try: - mi = np.ravel_multi_index(indx[1:], orig_slice, mode='raise') + mi = np.ravel_multi_index(indx[1:], orig_slice, + mode='raise') except: # This happens with 0-sized orig_slice (sometimes?) # here it is a ValueError, but indexing gives a: raise IndexError('invalid index into 0-sized') else: - mi = np.ravel_multi_index(indx[1:], orig_slice, mode='wrap') + mi = np.ravel_multi_index(indx[1:], orig_slice, + mode='wrap') else: # Maybe never happens... raise ValueError @@ -962,9 +1025,12 @@ class TestMultiIndexingAutomated(TestCase): # it is aligned to the left. This is probably correct for # consistency with arr[boolean_array,] also no broadcasting # is done at all - self._check_multi_index(self.a, (np.zeros_like(self.a, dtype=bool),)) - self._check_multi_index(self.a, (np.zeros_like(self.a, dtype=bool)[..., 0],)) - self._check_multi_index(self.a, (np.zeros_like(self.a, dtype=bool)[None, ...],)) + self._check_multi_index( + self.a, (np.zeros_like(self.a, dtype=bool),)) + self._check_multi_index( + self.a, (np.zeros_like(self.a, dtype=bool)[..., 0],)) + self._check_multi_index( + self.a, (np.zeros_like(self.a, dtype=bool)[None, ...],)) def test_multidim(self): # Automatically test combinations with complex indexes on 2nd (or 1st) @@ -1003,6 +1069,119 @@ class TestMultiIndexingAutomated(TestCase): for index in self.complex_indices: self._check_single_index(a, index) +class TestFloatNonIntegerArgument(TestCase): + """ + These test that ``TypeError`` is raised when you try to use + non-integers as arguments to for indexing and slicing e.g. ``a[0.0:5]`` + and ``a[0.5]``, or other functions like ``array.reshape(1., -1)``. + + """ + def test_valid_indexing(self): + # These should raise no errors. + a = np.array([[[5]]]) + + a[np.array([0])] + a[[0, 0]] + a[:, [0, 0]] + a[:, 0,:] + a[:,:,:] + + def test_valid_slicing(self): + # These should raise no errors. + a = np.array([[[5]]]) + + a[::] + a[0:] + a[:2] + a[0:2] + a[::2] + a[1::2] + a[:2:2] + a[1:2:2] + + def test_non_integer_argument_errors(self): + a = np.array([[5]]) + + assert_raises(TypeError, np.reshape, a, (1., 1., -1)) + assert_raises(TypeError, np.reshape, a, (np.array(1.), -1)) + assert_raises(TypeError, np.take, a, [0], 1.) + assert_raises(TypeError, np.take, a, [0], np.float64(1.)) + + def test_non_integer_sequence_multiplication(self): + # Numpy scalar sequence multiply should not work with non-integers + def mult(a, b): + return a * b + + assert_raises(TypeError, mult, [1], np.float_(3)) + # following should be OK + mult([1], np.int_(3)) + + def test_reduce_axis_float_index(self): + d = np.zeros((3,3,3)) + assert_raises(TypeError, np.min, d, 0.5) + assert_raises(TypeError, np.min, d, (0.5, 1)) + assert_raises(TypeError, np.min, d, (1, 2.2)) + assert_raises(TypeError, np.min, d, (.2, 1.2)) + + +class TestBooleanArgumentErrors(TestCase): + """Using a boolean as integer argument/indexing is an error. + + """ + def test_bool_as_int_argument(self): + a = np.array([[[1]]]) + + assert_raises(TypeError, np.reshape, a, (True, -1)) + assert_raises(TypeError, np.reshape, a, (np.bool_(True), -1)) + # Note that operator.index(np.array(True)) does not work, a boolean + # array is thus also deprecated, but not with the same message: + assert_raises(TypeError, operator.index, np.array(True)) + assert_raises(TypeError, np.take, args=(a, [0], False)) + assert_raises(IndexError, lambda: a[False:True:True]) + assert_raises(IndexError, lambda: a[False, 0]) + assert_raises(IndexError, lambda: a[False, 0, 0]) + + +class TestArrayToIndexDeprecation(TestCase): + """Creating an an index from array not 0-D is an error. + + """ + def test_array_to_index_error(self): + # so no exception is expected. The raising is effectively tested above. + a = np.array([[[1]]]) + + assert_raises(TypeError, operator.index, np.array([1])) + assert_raises(TypeError, np.reshape, a, (a, -1)) + assert_raises(TypeError, np.take, a, [0], a) + + +class TestNonIntegerArrayLike(TestCase): + """Tests that array_likes only valid if can safely cast to integer. + + For instance, lists give IndexError when they cannot be safely cast to + an integer. + + """ + def test_basic(self): + a = np.arange(10) + + assert_raises(IndexError, a.__getitem__, [0.5, 1.5]) + assert_raises(IndexError, a.__getitem__, (['1', '2'],)) + + # The following is valid + a.__getitem__([]) + + +class TestMultipleEllipsisError(TestCase): + """An index can only have a single ellipsis. + + """ + def test_basic(self): + a = np.arange(10) + assert_raises(IndexError, lambda: a[..., ...]) + assert_raises(IndexError, a.__getitem__, ((Ellipsis,) * 2,)) + assert_raises(IndexError, a.__getitem__, ((Ellipsis,) * 3,)) + class TestCApiAccess(TestCase): def test_getitem(self): diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 6a9d76a27..5e758fb89 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1913,19 +1913,25 @@ class TestBincount(TestCase): def test_with_incorrect_minlength(self): x = np.array([], dtype=int) - assert_raises_regex(TypeError, "an integer is required", + assert_raises_regex(TypeError, + "'str' object cannot be interpreted", lambda: np.bincount(x, minlength="foobar")) - assert_raises_regex(ValueError, "must be positive", + assert_raises_regex(ValueError, + "must be positive", lambda: np.bincount(x, minlength=-1)) - assert_raises_regex(ValueError, "must be positive", + assert_raises_regex(ValueError, + "must be positive", lambda: np.bincount(x, minlength=0)) x = np.arange(5) - assert_raises_regex(TypeError, "an integer is required", + assert_raises_regex(TypeError, + "'str' object cannot be interpreted", lambda: np.bincount(x, minlength="foobar")) - assert_raises_regex(ValueError, "minlength must be positive", + assert_raises_regex(ValueError, + "minlength must be positive", lambda: np.bincount(x, minlength=-1)) - assert_raises_regex(ValueError, "minlength must be positive", + assert_raises_regex(ValueError, + "minlength must be positive", lambda: np.bincount(x, minlength=0)) |