diff options
author | Charles Harris <charlesr.harris@gmail.com> | 2013-06-09 08:11:02 -0700 |
---|---|---|
committer | Charles Harris <charlesr.harris@gmail.com> | 2013-06-09 08:11:02 -0700 |
commit | 558cd20c29db0cd89c4d92fc354602650f861064 (patch) | |
tree | ad3531de30581e88b146aecbce8c057e4f792d30 /numpy | |
parent | 704b456e49b13150aa693f4fd7d66dca0d541f0b (diff) | |
parent | 163f6df5a6668d06cb7abfe38dbd03d19b26d6f3 (diff) | |
download | numpy-558cd20c29db0cd89c4d92fc354602650f861064.tar.gz |
Merge pull request #3243 from seberg/deprecate-non-integer-arguments-new
Deprecate non integer arguments
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/defchararray.py | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/conversion_utils.c | 366 | ||||
-rw-r--r-- | numpy/core/src/multiarray/conversion_utils.h | 3 | ||||
-rw-r--r-- | numpy/core/src/multiarray/iterators.c | 53 | ||||
-rw-r--r-- | numpy/core/src/multiarray/mapping.c | 51 | ||||
-rw-r--r-- | numpy/core/src/multiarray/number.c | 6 | ||||
-rw-r--r-- | numpy/core/src/private/npy_pycompat.h | 12 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 295 | ||||
-rw-r--r-- | numpy/core/tests/test_indexing.py | 5 | ||||
-rw-r--r-- | numpy/lib/index_tricks.py | 2 | ||||
-rw-r--r-- | numpy/lib/shape_base.py | 2 | ||||
-rw-r--r-- | numpy/random/tests/test_regression.py | 2 |
12 files changed, 387 insertions, 414 deletions
diff --git a/numpy/core/defchararray.py b/numpy/core/defchararray.py index 2a8b31616..92995baa1 100644 --- a/numpy/core/defchararray.py +++ b/numpy/core/defchararray.py @@ -90,7 +90,7 @@ def _get_num_chars(a): for a unicode array this is itemsize / 4. """ if issubclass(a.dtype.type, unicode_): - return a.itemsize / 4 + return a.itemsize // 4 return a.itemsize @@ -2593,7 +2593,7 @@ def array(obj, itemsize=None, copy=True, unicode=None, order=None): # to divide by the size of a single Unicode character, # which for Numpy is always 4 if issubclass(obj.dtype.type, unicode_): - itemsize /= 4 + itemsize //= 4 if unicode is None: if issubclass(obj.dtype.type, unicode_): diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index 27d435881..79093467f 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -84,7 +84,7 @@ PyArray_OutputConverter(PyObject *object, PyArrayObject **address) NPY_NO_EXPORT int PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) { - int len; + Py_ssize_t len; int nd; seq->ptr = NULL; @@ -94,14 +94,18 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) } len = PySequence_Size(obj); if (len == -1) { - /* Check to see if it is a number */ + /* Check to see if it is an integer number */ if (PyNumber_Check(obj)) { + /* + * After the deprecation the PyNumber_Check could be replaced + * by PyIndex_Check. + */ len = 1; } } if (len < 0) { PyErr_SetString(PyExc_TypeError, - "expected sequence object with len >= 0"); + "expected sequence object with len >= 0 or a single integer"); return NPY_FAIL; } if (len > NPY_MAXDIMS) { @@ -117,7 +121,7 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) } } seq->len = len; - nd = PyArray_IntpFromSequence(obj, (npy_intp *)seq->ptr, len); + nd = PyArray_IntpFromIndexSequence(obj, (npy_intp *)seq->ptr, len); if (nd == -1 || nd != len) { PyDimMem_FREE(seq->ptr); seq->ptr = NULL; @@ -187,7 +191,7 @@ PyArray_AxisConverter(PyObject *obj, int *axis) *axis = NPY_MAXDIMS; } else { - *axis = (int) PyInt_AsLong(obj); + *axis = PyArray_PyIntAsInt(obj); if (PyErr_Occurred()) { return NPY_FAIL; } @@ -223,9 +227,9 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) } for (i = 0; i < naxes; ++i) { PyObject *tmp = PyTuple_GET_ITEM(axis_in, i); - long axis = PyInt_AsLong(tmp); - long axis_orig = axis; - if (axis == -1 && PyErr_Occurred()) { + int axis = PyArray_PyIntAsInt(tmp); + int axis_orig = axis; + if (error_converting(axis)) { return NPY_FAIL; } if (axis < 0) { @@ -233,7 +237,7 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) } if (axis < 0 || axis >= ndim) { PyErr_Format(PyExc_ValueError, - "'axis' entry %ld is out of bounds [-%d, %d)", + "'axis' entry %d is out of bounds [-%d, %d)", axis_orig, ndim, ndim); return NPY_FAIL; } @@ -249,14 +253,14 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) } /* Try to interpret axis as an integer */ else { - long axis, axis_orig; + int axis, axis_orig; memset(out_axis_flags, 0, ndim); - axis = PyInt_AsLong(axis_in); + axis = PyArray_PyIntAsInt(axis_in); axis_orig = axis; - /* TODO: PyNumber_Index would be good to use here */ - if (axis == -1 && PyErr_Occurred()) { + + if (error_converting(axis)) { return NPY_FAIL; } if (axis < 0) { @@ -272,7 +276,7 @@ PyArray_ConvertMultiAxis(PyObject *axis_in, int ndim, npy_bool *out_axis_flags) if (axis < 0 || axis >= ndim) { PyErr_Format(PyExc_ValueError, - "'axis' entry %ld is out of bounds [-%d, %d)", + "'axis' entry %d is out of bounds [-%d, %d)", axis_orig, ndim, ndim); return NPY_FAIL; } @@ -529,8 +533,8 @@ PyArray_ClipmodeConverter(PyObject *object, NPY_CLIPMODE *val) return ret; } else { - int number = PyInt_AsLong(object); - if (number == -1 && PyErr_Occurred()) { + int number = PyArray_PyIntAsInt(object); + if (error_converting(number)) { goto fail; } if (number <= (int) NPY_RAISE @@ -664,85 +668,11 @@ PyArray_CastingConverter(PyObject *obj, NPY_CASTING *casting) NPY_NO_EXPORT int PyArray_PyIntAsInt(PyObject *o) { - long long_value = -1; - PyObject *obj; - static char *msg = "an integer is required"; - PyArrayObject *arr; - PyArray_Descr *descr; - int ret; - - - if (!o) { - PyErr_SetString(PyExc_TypeError, msg); - return -1; - } - if (PyInt_Check(o)) { - long_value = (long) PyInt_AS_LONG(o); - goto finish; - } else if (PyLong_Check(o)) { - long_value = (long) PyLong_AsLong(o); - goto finish; - } - - descr = &INT_Descr; - arr = NULL; - if (PyArray_Check(o)) { - if (PyArray_SIZE((PyArrayObject *)o)!=1 || - !PyArray_ISINTEGER((PyArrayObject *)o)) { - PyErr_SetString(PyExc_TypeError, msg); - return -1; - } - Py_INCREF(descr); - arr = (PyArrayObject *)PyArray_CastToType((PyArrayObject *)o, - descr, 0); - } - if (PyArray_IsScalar(o, Integer)) { - Py_INCREF(descr); - arr = (PyArrayObject *)PyArray_FromScalar(o, descr); - } - if (arr != NULL) { - ret = *((int *)PyArray_DATA(arr)); - Py_DECREF(arr); - return ret; - } -#if (PY_VERSION_HEX >= 0x02050000) - if (PyIndex_Check(o)) { - PyObject* value = PyNumber_Index(o); - long_value = (npy_longlong) PyInt_AsSsize_t(value); - goto finish; - } -#endif - 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; - } - long_value = (long) PyLong_AsLong(obj); - 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; - } - long_value = (long) PyLong_AsLong(obj); - Py_DECREF(obj); - } -#endif - else { - PyErr_SetString(PyExc_NotImplementedError,""); - } - - finish: - if error_converting(long_value) { - PyErr_SetString(PyExc_TypeError, msg); - return -1; - } + npy_intp long_value; + /* This assumes that NPY_SIZEOF_INTP >= NPY_SIZEOF_INT */ + long_value = PyArray_PyIntAsIntp(o); -#if (NPY_SIZEOF_LONG > NPY_SIZEOF_INT) +#if (NPY_SIZEOF_INTP > NPY_SIZEOF_INT) if ((long_value < INT_MIN) || (long_value > INT_MAX)) { PyErr_SetString(PyExc_ValueError, "integer won't fit into a C int"); return -1; @@ -755,145 +685,188 @@ PyArray_PyIntAsInt(PyObject *o) NPY_NO_EXPORT npy_intp PyArray_PyIntAsIntp(PyObject *o) { +#if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) + npy_long long_value = -1; +#else npy_longlong long_value = -1; - PyObject *obj; +#endif + PyObject *obj, *err; static char *msg = "an integer is required"; - PyArrayObject *arr; - PyArray_Descr *descr; - npy_intp ret; if (!o) { PyErr_SetString(PyExc_TypeError, msg); return -1; } - if (PyInt_Check(o)) { - long_value = (npy_longlong) PyInt_AS_LONG(o); - goto finish; - } else if (PyLong_Check(o)) { - long_value = (npy_longlong) PyLong_AsLongLong(o); - goto finish; + + /* Be a bit stricter and not allow bools, np.bool_ is handled later */ + if (PyBool_Check(o)) { + if (DEPRECATE("using a boolean instead of an integer" + " will result in an error in the future") < 0) { + return -1; + } } -#if NPY_SIZEOF_INTP == NPY_SIZEOF_LONG - descr = &LONG_Descr; -#elif NPY_SIZEOF_INTP == NPY_SIZEOF_INT - descr = &INT_Descr; + /* + * Since it is the usual case, first check if o is an integer. This is + * an exact check, since otherwise __index__ is used. + */ +#if !defined(NPY_PY3K) + if PyInt_CheckExact(o) { + #if (NPY_SIZEOF_LONG <= NPY_SIZEOF_INTP) + /* No overflow is possible, so we can just return */ + return PyInt_AS_LONG(o); + #else + long_value = PyInt_AS_LONG(o); + goto overflow_check; + #endif + } + else +#endif + if PyLong_CheckExact(o) { +#if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) + long_value = PyLong_AsLongLong(o); #else - descr = &LONGLONG_Descr; + long_value = PyLong_AsLong(o); #endif - arr = NULL; + return (npy_intp)long_value; + } - if (PyArray_Check(o)) { - if (PyArray_SIZE((PyArrayObject *)o)!=1 || - !PyArray_ISINTEGER((PyArrayObject *)o)) { - PyErr_SetString(PyExc_TypeError, msg); + /* Disallow numpy.bool_. Boolean arrays do not currently support index. */ + if (PyArray_IsScalar(o, Bool)) { + if (DEPRECATE("using a boolean instead of an integer" + " will result in an error in the future") < 0) { return -1; } - Py_INCREF(descr); - arr = (PyArrayObject *)PyArray_CastToType((PyArrayObject *)o, - descr, 0); } - else if (PyArray_IsScalar(o, Integer)) { - Py_INCREF(descr); - arr = (PyArrayObject *)PyArray_FromScalar(o, descr); + + /* + * 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 (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) + long_value = PyLong_AsLongLong(obj); +#else + long_value = PyLong_AsLong(obj); +#endif + Py_DECREF(obj); + goto finish; } - if (arr != NULL) { - ret = *((npy_intp *)PyArray_DATA(arr)); - Py_DECREF(arr); - return ret; + else { + /* + * Set the TypeError like PyNumber_Index(o) would after trying + * the general case. + */ + PyErr_Clear(); } -#if (PY_VERSION_HEX >= 0x02050000) - if (PyIndex_Check(o)) { - PyObject* value = PyNumber_Index(o); - if (value == NULL) { + /* + * 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; } - long_value = (npy_longlong) PyInt_AsSsize_t(value); - goto finish; + #if (NPY_SIZEOF_LONG < NPY_SIZEOF_INTP) + long_value = PyLong_AsLongLong(obj); + #else + long_value = PyLong_AsLong(obj); + #endif + Py_DECREF(obj); } -#endif #if !defined(NPY_PY3K) - if (Py_TYPE(o)->tp_as_number != NULL && \ - Py_TYPE(o)->tp_as_number->nb_long != NULL) { + 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) { - long_value = (npy_longlong) PyLong_AsLongLong(obj); - Py_DECREF(obj); + 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); } - else #endif - 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) { - long_value = (npy_longlong) PyLong_AsLongLong(obj); - Py_DECREF(obj); - } - } else { - PyErr_SetString(PyExc_NotImplementedError,""); + PyErr_SetString(PyExc_TypeError, msg); + return -1; + } + /* Give a deprecation warning, unless there was already an error */ + if (!error_converting(long_value)) { + 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) { + if (error_converting(long_value)) { + err = PyErr_Occurred(); + /* Only replace TypeError's here, which are the normal errors. */ + if (PyErr_GivenExceptionMatches(err, PyExc_TypeError)) { PyErr_SetString(PyExc_TypeError, msg); - return -1; } + return -1; + } -#if (NPY_SIZEOF_LONGLONG > NPY_SIZEOF_INTP) + 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)) { - PyErr_SetString(PyExc_ValueError, - "integer won't fit into a C intp"); + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C numpy.intp"); return -1; } + #endif +#else + #if (NPY_SIZEOF_LONG > NPY_SIZEOF_INTP) + if ((long_value < NPY_MIN_INTP) || (long_value > NPY_MAX_INTP)) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C numpy.intp"); + return -1; + } + #endif #endif - return (npy_intp) long_value; + return long_value; } -/*NUMPY_API - * PyArray_IntpFromSequence + +/* + * PyArray_IntpFromIndexSequence * Returns the number of dimensions or -1 if an error occurred. - * vals must be large enough to hold maxvals + * vals must be large enough to hold maxvals. + * Opposed to PyArray_IntpFromSequence it uses and returns npy_intp + * for the number of values. */ -NPY_NO_EXPORT int -PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals) +NPY_NO_EXPORT npy_intp +PyArray_IntpFromIndexSequence(PyObject *seq, npy_intp *vals, npy_intp maxvals) { - int nd, i; + Py_ssize_t nd; + npy_intp i; PyObject *op, *err; /* * Check to see if sequence is a single integer first. * or, can be made into one */ - if ((nd=PySequence_Length(seq)) == -1) { - if (PyErr_Occurred()) PyErr_Clear(); -#if NPY_SIZEOF_LONG >= NPY_SIZEOF_INTP && !defined(NPY_PY3K) - if (!(op = PyNumber_Int(seq))) { - return -1; - } -#else - if (!(op = PyNumber_Long(seq))) { - return -1; + nd = PySequence_Length(seq); + if (nd == -1) { + if (PyErr_Occurred()) { + PyErr_Clear(); } -#endif - nd = 1; -#if NPY_SIZEOF_LONG >= NPY_SIZEOF_INTP - vals[0] = (npy_intp ) PyInt_AsLong(op); -#else - vals[0] = (npy_intp ) PyLong_AsLongLong(op); -#endif - Py_DECREF(op); - /* - * Check wether there was an error - if the error was an overflow, raise - * a ValueError instead to be more helpful - */ + vals[0] = PyArray_PyIntAsIntp(seq); if(vals[0] == -1) { err = PyErr_Occurred(); - if (err && - PyErr_GivenExceptionMatches(err, PyExc_OverflowError)) { + if (err && + PyErr_GivenExceptionMatches(err, PyExc_OverflowError)) { PyErr_SetString(PyExc_ValueError, "Maximum allowed dimension exceeded"); } @@ -901,6 +874,7 @@ PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals) return -1; } } + nd = 1; } else { for (i = 0; i < PyArray_MIN(nd,maxvals); i++) { @@ -908,21 +882,13 @@ PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals) if (op == NULL) { return -1; } -#if NPY_SIZEOF_LONG >= NPY_SIZEOF_INTP - vals[i]=(npy_intp )PyInt_AsLong(op); -#else - vals[i]=(npy_intp )PyLong_AsLongLong(op); -#endif - Py_DECREF(op); - /* - * Check wether there was an error - if the error was an overflow, - * raise a ValueError instead to be more helpful - */ - if(vals[0] == -1) { + + vals[i] = PyArray_PyIntAsIntp(op); + if(vals[i] == -1) { err = PyErr_Occurred(); - if (err && - PyErr_GivenExceptionMatches(err, PyExc_OverflowError)) { + if (err && + PyErr_GivenExceptionMatches(err, PyExc_OverflowError)) { PyErr_SetString(PyExc_ValueError, "Maximum allowed dimension exceeded"); } @@ -935,6 +901,18 @@ PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals) return nd; } +/*NUMPY_API + * PyArray_IntpFromSequence + * Returns the number of integers converted or -1 if an error occurred. + * vals must be large enough to hold maxvals + */ +NPY_NO_EXPORT int +PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals) +{ + return PyArray_IntpFromIndexSequence(seq, vals, (npy_intp)maxvals); +} + + /** * WARNING: This flag is a bad idea, but was the only way to both * 1) Support unpickling legacy pickles with object types. diff --git a/numpy/core/src/multiarray/conversion_utils.h b/numpy/core/src/multiarray/conversion_utils.h index baa110e90..cc16cd9c7 100644 --- a/numpy/core/src/multiarray/conversion_utils.h +++ b/numpy/core/src/multiarray/conversion_utils.h @@ -25,6 +25,9 @@ PyArray_PyIntAsInt(PyObject *o); NPY_NO_EXPORT npy_intp PyArray_PyIntAsIntp(PyObject *o); +NPY_NO_EXPORT npy_intp +PyArray_IntpFromIndexSequence(PyObject *seq, npy_intp *vals, npy_intp maxvals); + NPY_NO_EXPORT int PyArray_IntpFromSequence(PyObject *seq, npy_intp *vals, int maxvals); diff --git a/numpy/core/src/multiarray/iterators.c b/numpy/core/src/multiarray/iterators.c index abaff6c98..4584d0c60 100644 --- a/numpy/core/src/multiarray/iterators.c +++ b/numpy/core/src/multiarray/iterators.c @@ -70,12 +70,6 @@ parse_index_entry(PyObject *op, npy_intp *step_size, } *n_steps = SINGLE_INDEX; *step_size = 0; - if (!PyIndex_Check_Or_Unsupported(op)) { - if (DEPRECATE("non-integer scalar index. In a future numpy " - "release, this will raise an error.") < 0) { - goto fail; - } - } if (check_index) { if (check_and_adjust_index(&i, max, axis) < 0) { goto fail; @@ -204,26 +198,8 @@ parse_index(PyArrayObject *self, PyObject *op, static int slice_coerce_index(PyObject *o, npy_intp *v) { - /* - * PyNumber_Index was introduced in Python 2.5 because of NumPy. - * http://www.python.org/dev/peps/pep-0357/ - * Let's use it for indexing! - * - * Unfortunately, SciPy and possibly other code seems to rely - * on the lenient coercion. :( - */ -#if 0 /*PY_VERSION_HEX >= 0x02050000*/ - PyObject *ind = PyNumber_Index(o); - if (ind != NULL) { - *v = PyArray_PyIntAsIntp(ind); - Py_DECREF(ind); - } - else { - *v = -1; - } -#else *v = PyArray_PyIntAsIntp(o); -#endif + if ((*v) == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; @@ -231,24 +207,6 @@ slice_coerce_index(PyObject *o, npy_intp *v) return 1; } -/* - * Issue a DeprecationWarning for slice parameters that do not pass a - * PyIndex_Check, returning -1 if an error occurs. - * - * N.B. This function, like slice_GetIndices, will be obsolete once - * non-integer slice parameters becomes an error rather than a warning. - */ -static NPY_INLINE int -_validate_slice_parameter(PyObject *o) -{ - if (!PyIndex_Check_Or_Unsupported(o)) { - if (DEPRECATE("non-integer slice parameter. In a future numpy " - "release, this will raise an error.") < 0) { - return -1; - } - } - return 0; -} /* * This is basically PySlice_GetIndicesEx, but with our coercion @@ -268,9 +226,6 @@ slice_GetIndices(PySliceObject *r, npy_intp length, *step = 1; } else { - if (_validate_slice_parameter(r->step) < 0) { - return -1; - } if (!slice_coerce_index(r->step, step)) { return -1; } @@ -286,9 +241,6 @@ slice_GetIndices(PySliceObject *r, npy_intp length, *start = *step < 0 ? length-1 : 0; } else { - if (_validate_slice_parameter(r->start) < 0) { - return -1; - } if (!slice_coerce_index(r->start, start)) { return -1; } @@ -307,9 +259,6 @@ slice_GetIndices(PySliceObject *r, npy_intp length, *stop = defstop; } else { - if (_validate_slice_parameter(r->stop) < 0) { - return -1; - } if (!slice_coerce_index(r->stop, stop)) { return -1; } diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c index 17089761d..21874f8a9 100644 --- a/numpy/core/src/multiarray/mapping.c +++ b/numpy/core/src/multiarray/mapping.c @@ -560,33 +560,17 @@ array_subscript_simple(PyArrayObject *self, PyObject *op, int check_index) PyArrayObject *ret; npy_intp value; - /* - * PyNumber_Index was introduced in Python 2.5 because of NumPy. - * http://www.python.org/dev/peps/pep-0357/ - * Let's use it for indexing! - * - * Unfortunately, SciPy and possibly other code seems to rely - * on the lenient coercion. :( - */ if (!(PyArray_Check(op) && (PyArray_SIZE((PyArrayObject*)op) > 1))) { -#if 0 /*PY_VERSION_HEX >= 0x02050000*/ - PyObject *ind = PyNumber_Index(op); - if (ind != NULL) { - value = PyArray_PyIntAsIntp(ind); - Py_DECREF(ind); - } - else { - value = -1; - } -#else value = PyArray_PyIntAsIntp(op); -#endif + if (value == -1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { /* Operand is not an integer type */ PyErr_Clear(); } else { + PyErr_SetString(PyExc_IndexError, + "cannot convert index to integer"); return NULL; } } @@ -940,7 +924,6 @@ _is_full_index(PyObject *ind, PyArrayObject *arr) * Returns 0 if tuple-object seq is not a tuple of integers. * If the return value is positive, vals will be filled with the elements * from the tuple. - * Returns -1 on error. */ static int _tuple_of_integers(PyObject *seq, npy_intp *vals, int maxvals) @@ -960,12 +943,6 @@ _tuple_of_integers(PyObject *seq, npy_intp *vals, int maxvals) PyErr_Clear(); return 0; } - if (!PyIndex_Check_Or_Unsupported(obj)) { - if (DEPRECATE("non-integer scalar index. In a future numpy " - "release, this will raise an error.") < 0) { - return -1; - } - } vals[i] = temp; } return 1; @@ -1070,11 +1047,8 @@ array_subscript_fromobject(PyArrayObject *self, PyObject *op) /* optimization for a tuple of integers */ if (PyArray_NDIM(self) > 1 && _is_full_index(op, self)) { int ret = _tuple_of_integers(op, vals, PyArray_NDIM(self)); - /* In case an exception occurred (e.g. in PyErr_WarnEx) */ - if (ret < 0) { - return NULL; - } - else if (ret > 0) { + + if (ret > 0) { int idim, ndim = PyArray_NDIM(self); npy_intp *shape = PyArray_DIMS(self); npy_intp *strides = PyArray_STRIDES(self); @@ -1090,14 +1064,6 @@ array_subscript_fromobject(PyArrayObject *self, PyObject *op) } } - if ((PyNumber_Check(op) || PyArray_IsScalar(op, Number)) && - !PyIndex_Check_Or_Unsupported(op)) { - if (DEPRECATE("non-integer scalar index. In a future numpy " - "release, this will raise an error.") < 0) { - return NULL; - } - } - /* Check for single field access */ if (PyString_Check(op) || PyUnicode_Check(op)) { PyObject *temp, *obj; @@ -1472,11 +1438,8 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op) /* Integer-tuple index */ if (_is_full_index(ind, self)) { int ret = _tuple_of_integers(ind, vals, PyArray_NDIM(self)); - /* In case an exception occurred (e.g. in PyErr_WarnEx) */ - if (ret < 0) { - return -1; - } - else if (ret > 0) { + + if (ret > 0) { int idim, ndim = PyArray_NDIM(self); npy_intp *shape = PyArray_DIMS(self); npy_intp *strides = PyArray_STRIDES(self); diff --git a/numpy/core/src/multiarray/number.c b/numpy/core/src/multiarray/number.c index c37a232f4..9e13d0f29 100644 --- a/numpy/core/src/multiarray/number.c +++ b/numpy/core/src/multiarray/number.c @@ -840,6 +840,12 @@ array_index(PyArrayObject *v) "one element can be converted to an index"); return NULL; } + if (PyArray_NDIM(v) != 0) { + 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); } #endif diff --git a/numpy/core/src/private/npy_pycompat.h b/numpy/core/src/private/npy_pycompat.h index 2c76d68f4..8f596e75a 100644 --- a/numpy/core/src/private/npy_pycompat.h +++ b/numpy/core/src/private/npy_pycompat.h @@ -13,16 +13,4 @@ #define Py_SIZE(o) (((PyVarObject*)(o))->ob_size) #endif -/* - * PyIndex_Check - */ -#if (PY_VERSION_HEX < 0x02050000) -#undef PyIndex_Check -#define PyIndex_Check(o) 0 -#undef PyIndex_Check_Or_Unsupported -#define PyIndex_Check_Or_Unsupported(o) 1 -#else -#define PyIndex_Check_Or_Unsupported(o) PyIndex_Check(o) -#endif - #endif /* _NPY_COMPAT_H_ */ diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 9b77a1fc1..2bedbca09 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -6,6 +6,7 @@ to document how deprecations should eventually be turned into errors. from __future__ import division, absolute_import, print_function import sys +import operator import warnings from nose.plugins.skip import SkipTest @@ -13,58 +14,117 @@ import numpy as np from numpy.testing import dec, run_module_suite, assert_raises -def assert_deprecated(f, *args, **kwargs): - """Check if DeprecationWarning raised as error. +class _DeprecationTestCase(object): + # Just as warning: warnings uses re.match, so the start of this message + # must match. + message = '' - The warning environment is assumed to have been set up so that the - appropriate DeprecationWarning has been turned into an error. We do not - use assert_warns here as the desire is to check that an error will be - raised if the deprecation is changed to an error and there may be other - errors that would override a warning. It is a fine point as to which - error should appear first. - - Parameters - ---------- - f : callable - A function that will exhibit the deprecation. It need not be - deprecated itself, but can be used to execute deprecated code. - - """ - assert_raises(DeprecationWarning, f, *args, **kwargs) - - -def assert_not_deprecated(f, *args, **kwargs): - """Check that DeprecationWarning not raised as error. - - The warning environment is assumed to have been set up so that the - appropriate DeprecationWarning has been turned into an error. This - function checks that no warning is raised when `f` is executed. - - Parameters - ---------- - f : callable - A function that will exhibit no deprecation. It can be used to - execute code that should not raise DeprecationWarning. + def setUp(self): + self.warn_ctx = warnings.catch_warnings(record=True) + self.log = self.warn_ctx.__enter__() - """ - try: - f(*args, **kwargs) - except DeprecationWarning: - raise AssertionError() + # Do *not* ignore other DeprecationWarnings. Ignoring warnings + # can give very confusing results because of + # http://bugs.python.org/issue4180 and it is probably simplest to + # try to keep the tests cleanly giving only the right warning type. + # (While checking them set to "error" those are ignored anyway) + # We still have them show up, because otherwise they would be raised + warnings.filterwarnings("always", category=DeprecationWarning) + warnings.filterwarnings("always", message=self.message, + category=DeprecationWarning) -class TestFloatScalarIndexDeprecation(object): + def tearDown(self): + self.warn_ctx.__exit__() + + + def assert_deprecated(self, function, num=1, ignore_others=False, + function_fails=False, + exceptions=(DeprecationWarning,), args=(), kwargs={}): + """Test if DeprecationWarnings are given and raised. + + This first checks if the function when called gives `num` + DeprecationWarnings, after that it tries to raise these + DeprecationWarnings and compares them with `exceptions`. + The exceptions can be different for cases where this code path + is simply not anticipated and the exception is replaced. + + Parameters + ---------- + f : callable + The function to test + num : int + Number of DeprecationWarnings to expect. This should normally be 1. + ignore_other : bool + Whether warnings of the wrong type should be ignored (note that + the message is not checked) + function_fails : bool + If the function would normally fail, setting this will check for + warnings inside a try/except block. + exceptions : Exception or tuple of Exceptions + Exception to expect when turning the warnings into an error. + The default checks for DeprecationWarnings. If exceptions is + empty the function is expected to run successfull. + args : tuple + Arguments for `f` + kwargs : dict + Keyword arguments for `f` + """ + # reset the log + self.log[:] = [] + + try: + function(*args, **kwargs) + except (Exception if function_fails else tuple()): + pass + # just in case, clear the registry + num_found = 0 + for warning in self.log: + if warning.category is DeprecationWarning: + num_found += 1 + elif not ignore_others: + raise AssertionError("expected DeprecationWarning but %s given" + % warning.category) + if num_found != num: + raise AssertionError("%i warnings found but %i expected" + % (len(self.log), num)) + + with warnings.catch_warnings(): + warnings.filterwarnings("error", message=self.message, + category=DeprecationWarning) + + try: + function(*args, **kwargs) + if exceptions != tuple(): + raise AssertionError("No error raised during function call") + except exceptions: + if exceptions == tuple(): + raise AssertionError("Error raised during function call") + + + def assert_not_deprecated(self, function, args=(), kwargs={}): + """Test if DeprecationWarnings are given and raised. + + This is just a shorthand for: + + self.assert_deprecated(function, num=0, ignore_others=True, + exceptions=tuple(), args=args, kwargs=kwargs) + """ + self.assert_deprecated(function, num=0, ignore_others=True, + exceptions=tuple(), args=args, kwargs=kwargs) + + +class TestFloatNonIntegerArgumentDeprecation(_DeprecationTestCase): """ - These test that ``DeprecationWarning`` gets raised when you try to use - scalar indices that are not integers e.g. ``a[0.0]``, ``a[1.5, 0]``. - - grep "non-integer scalar" numpy/core/src/multiarray/* for all the calls - to ``DEPRECATE()``, except the one inside ``_validate_slice_parameter`` - which handles slicing (but see also - `TestFloatSliceParameterDeprecation`). + 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)``. - When 2.4 support is dropped ``PyIndex_Check_Or_Unsupported`` should be - removed from ``npy_pycompat.h`` and changed to just ``PyIndex_Check``. + 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, @@ -72,31 +132,26 @@ class TestFloatScalarIndexDeprecation(object): 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. + 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 setUp(self): - warnings.filterwarnings("error", message="non-integer scalar index", - category=DeprecationWarning) - - - def tearDown(self): - warnings.filterwarnings("default", message="non-integer scalar index", - category=DeprecationWarning) - - - def test_deprecations(self): + 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]) 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, :, :], num=2) # [1] assert_deprecated(lambda: a[0, 0, 0.0]) assert_deprecated(lambda: a[0.0, 0, 0]) assert_deprecated(lambda: a[0, 0.0, 0]) @@ -106,18 +161,21 @@ class TestFloatScalarIndexDeprecation(object): 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[-1.4, :, :], num=2) # [1] assert_deprecated(lambda: a[0, 0, -1.4]) assert_deprecated(lambda: a[-1.4, 0, 0]) assert_deprecated(lambda: a[0, -1.4, 0]) + # [1] These are duplicate because of the _tuple_of_integers quick check + # Test that the slice parameter deprecation warning doesn't mask # the scalar index warning. - assert_deprecated(lambda: a[0.0:, 0.0]) - assert_deprecated(lambda: a[0.0:, 0.0, :]) + assert_deprecated(lambda: a[0.0:, 0.0], num=2) + assert_deprecated(lambda: a[0.0:, 0.0, :], num=2) - def test_valid_not_deprecated(self): + 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]]) @@ -126,40 +184,10 @@ class TestFloatScalarIndexDeprecation(object): assert_not_deprecated(lambda: a[:, :, :]) -class TestFloatSliceParameterDeprecation(object): - """ - These test that ``DeprecationWarning`` gets raised when you try to use - non-integers for slicing, e.g. ``a[0.0:5]``, ``a[::1.5]``, etc. - - When this is changed to an error, ``slice_GetIndices`` and - ``_validate_slice_parameter`` should probably be removed. Calls to - ``slice_GetIndices`` should be replaced by the standard Python API call - ``PySlice_GetIndicesEx``, since ``slice_GetIndices`` implements the - same thing but with int coercion and Python < 2.3 backwards - compatibility (which we have long since dropped as of this writing). - - 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. - - """ - - def setUp(self): - warnings.filterwarnings("error", message="non-integer slice param", - category=DeprecationWarning) - - - def tearDown(self): - warnings.filterwarnings("default", message="non-integer slice param", - category=DeprecationWarning) - - - def test_deprecations(self): + 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:]) @@ -180,18 +208,19 @@ class TestFloatSliceParameterDeprecation(object): assert_deprecated(lambda: a[::5.0, :]) assert_deprecated(lambda: a[:, 0:4:2.0]) # mixed. - assert_deprecated(lambda: a[1.0:2:2.0]) - assert_deprecated(lambda: a[1.0::2.0]) - assert_deprecated(lambda: a[0:, :2.0:2.0]) - assert_deprecated(lambda: a[1.0:1:4.0, :0]) - assert_deprecated(lambda: a[1.0:5.0:5.0, :]) - assert_deprecated(lambda: a[:, 0.4:4.0:2.0]) + 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]) + assert_deprecated(lambda: a[::0.0], function_fails=True) - def test_valid_not_deprecated(self): + 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:]) @@ -203,5 +232,61 @@ class TestFloatSliceParameterDeprecation(object): 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.))) + + +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) + + if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py index 0a81a70d0..6d498b81f 100644 --- a/numpy/core/tests/test_indexing.py +++ b/numpy/core/tests/test_indexing.py @@ -73,8 +73,9 @@ class TestIndexing(TestCase): [7, 8, 9]]) # Python boolean converts to integer - assert_equal(a[True], a[1]) - assert_equal(a[False], a[0]) + # These are being deprecated (and test in test_deprecations) + #assert_equal(a[True], a[1]) + #assert_equal(a[False], a[0]) # Same with NumPy boolean scalar assert_equal(a[np.array(True)], a[1]) diff --git a/numpy/lib/index_tricks.py b/numpy/lib/index_tricks.py index b3c9b72bc..ca3d60869 100644 --- a/numpy/lib/index_tricks.py +++ b/numpy/lib/index_tricks.py @@ -157,7 +157,7 @@ class nd_grid(object): size.append(int(abs(step))) typ = float else: - size.append(math.ceil((key[k].stop - start)/(step*1.0))) + size.append(int(math.ceil((key[k].stop - start)/(step*1.0)))) if isinstance(step, float) or \ isinstance(start, float) or \ isinstance(key[k].stop, float): diff --git a/numpy/lib/shape_base.py b/numpy/lib/shape_base.py index e81bae5fc..c63f8140d 100644 --- a/numpy/lib/shape_base.py +++ b/numpy/lib/shape_base.py @@ -830,5 +830,5 @@ def tile(A, reps): dim_in = shape[i] dim_out = dim_in*nrep shape[i] = dim_out - n /= max(dim_in,1) + n //= max(dim_in, 1) return c.reshape(shape) diff --git a/numpy/random/tests/test_regression.py b/numpy/random/tests/test_regression.py index 9f7455fe5..1bba5d91d 100644 --- a/numpy/random/tests/test_regression.py +++ b/numpy/random/tests/test_regression.py @@ -72,7 +72,7 @@ class TestRegression(TestCase): np.random.seed(i) m.seed(4321) # If m.state is not honored, the result will change - assert_array_equal(m.choice(10, size=10, p=np.ones(10.)/10), res) + assert_array_equal(m.choice(10, size=10, p=np.ones(10)/10.), res) def test_multivariate_normal_size_types(self): # Test for multivariate_normal issue with 'size' argument. |