Skip to content

Commit 7706227

Browse files
committed
Merge pull request numpy#2812 from dwf/deprecate_scalar_float
Deprecate non-integer scalar indices and slice parameters
2 parents 4f7d3ff + bb0952d commit 7706227

File tree

4 files changed

+297
-39
lines changed

4 files changed

+297
-39
lines changed

numpy/core/src/multiarray/iterators.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ parse_index_entry(PyObject *op, npy_intp *step_size,
7070
}
7171
*n_steps = SINGLE_INDEX;
7272
*step_size = 0;
73+
if (!PyIndex_Check_Or_Unsupported(op)) {
74+
if (DEPRECATE("non-integer scalar index. In a future numpy "
75+
"release, this will raise an error.") < 0) {
76+
goto fail;
77+
}
78+
}
7379
if (check_index) {
7480
if (check_and_adjust_index(&i, max, axis) < 0) {
7581
goto fail;
@@ -223,8 +229,32 @@ slice_coerce_index(PyObject *o, npy_intp *v)
223229
return 1;
224230
}
225231

226-
/* This is basically PySlice_GetIndicesEx, but with our coercion
227-
* of indices to integers (plus, that function is new in Python 2.3) */
232+
/*
233+
* Issue a DeprecationWarning for slice parameters that do not pass a
234+
* PyIndex_Check, returning -1 if an error occurs.
235+
*
236+
* N.B. This function, like slice_GetIndices, will be obsolete once
237+
* non-integer slice parameters becomes an error rather than a warning.
238+
*/
239+
static NPY_INLINE int
240+
_validate_slice_parameter(PyObject *o)
241+
{
242+
if (!PyIndex_Check_Or_Unsupported(o)) {
243+
if (DEPRECATE("non-integer slice parameter. In a future numpy "
244+
"release, this will raise an error.") < 0) {
245+
return -1;
246+
}
247+
}
248+
return 0;
249+
}
250+
251+
/*
252+
* This is basically PySlice_GetIndicesEx, but with our coercion
253+
* of indices to integers (plus, that function is new in Python 2.3)
254+
*
255+
* N.B. The coercion to integers is deprecated; once the DeprecationWarning
256+
* is changed to an error, it would seem that this is obsolete.
257+
*/
228258
NPY_NO_EXPORT int
229259
slice_GetIndices(PySliceObject *r, npy_intp length,
230260
npy_intp *start, npy_intp *stop, npy_intp *step,
@@ -236,6 +266,9 @@ slice_GetIndices(PySliceObject *r, npy_intp length,
236266
*step = 1;
237267
}
238268
else {
269+
if (_validate_slice_parameter(r->step) < 0) {
270+
return -1;
271+
}
239272
if (!slice_coerce_index(r->step, step)) {
240273
return -1;
241274
}
@@ -251,6 +284,9 @@ slice_GetIndices(PySliceObject *r, npy_intp length,
251284
*start = *step < 0 ? length-1 : 0;
252285
}
253286
else {
287+
if (_validate_slice_parameter(r->start) < 0) {
288+
return -1;
289+
}
254290
if (!slice_coerce_index(r->start, start)) {
255291
return -1;
256292
}
@@ -269,6 +305,9 @@ slice_GetIndices(PySliceObject *r, npy_intp length,
269305
*stop = defstop;
270306
}
271307
else {
308+
if (_validate_slice_parameter(r->stop) < 0) {
309+
return -1;
310+
}
272311
if (!slice_coerce_index(r->stop, stop)) {
273312
return -1;
274313
}

numpy/core/src/multiarray/mapping.c

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,10 +1129,19 @@ array_ass_sub_simple(PyArrayObject *self, PyObject *ind, PyObject *op)
11291129
return ret;
11301130
}
11311131

1132+
/* Check if ind is a tuple and if it has as many elements as arr has axes. */
1133+
static NPY_INLINE int
1134+
_is_full_index(PyObject *ind, PyArrayObject *arr)
1135+
{
1136+
return PyTuple_Check(ind) && (PyTuple_GET_SIZE(ind) == PyArray_NDIM(arr));
1137+
}
11321138

1133-
/* return -1 if tuple-object seq is not a tuple of integers.
1134-
otherwise fill vals with converted integers
1135-
*/
1139+
/*
1140+
* Returns 0 if tuple-object seq is not a tuple of integers.
1141+
* If the return value is positive, vals will be filled with the elements
1142+
* from the tuple.
1143+
* Returns -1 on error.
1144+
*/
11361145
static int
11371146
_tuple_of_integers(PyObject *seq, npy_intp *vals, int maxvals)
11381147
{
@@ -1144,15 +1153,21 @@ _tuple_of_integers(PyObject *seq, npy_intp *vals, int maxvals)
11441153
obj = PyTuple_GET_ITEM(seq, i);
11451154
if ((PyArray_Check(obj) && PyArray_NDIM((PyArrayObject *)obj) > 0)
11461155
|| PyList_Check(obj)) {
1147-
return -1;
1156+
return 0;
11481157
}
11491158
temp = PyArray_PyIntAsIntp(obj);
11501159
if (error_converting(temp)) {
1151-
return -1;
1160+
return 0;
1161+
}
1162+
if (!PyIndex_Check_Or_Unsupported(obj)) {
1163+
if (DEPRECATE("non-integer scalar index. In a future numpy "
1164+
"release, this will raise an error.") < 0) {
1165+
return -1;
1166+
}
11521167
}
11531168
vals[i] = temp;
11541169
}
1155-
return 0;
1170+
return 1;
11561171
}
11571172

11581173

@@ -1257,22 +1272,26 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op)
12571272
}
12581273

12591274
/* Integer-tuple */
1260-
if (PyTuple_Check(ind) &&
1261-
(PyTuple_GET_SIZE(ind) == PyArray_NDIM(self)) &&
1262-
(_tuple_of_integers(ind, vals, PyArray_NDIM(self)) >= 0)) {
1263-
int idim, ndim = PyArray_NDIM(self);
1264-
npy_intp *shape = PyArray_DIMS(self);
1265-
npy_intp *strides = PyArray_STRIDES(self);
1266-
char *item = PyArray_DATA(self);
1267-
1268-
for (idim = 0; idim < ndim; idim++) {
1269-
npy_intp v = vals[idim];
1270-
if (check_and_adjust_index(&v, shape[idim], idim) < 0) {
1271-
return -1;
1275+
if (_is_full_index(ind, self)) {
1276+
ret = _tuple_of_integers(ind, vals, PyArray_NDIM(self));
1277+
/* In case an exception occurred (e.g. in PyErr_WarnEx) */
1278+
if (ret < 0) {
1279+
return -1;
1280+
}
1281+
else if (ret > 0) {
1282+
int idim, ndim = PyArray_NDIM(self);
1283+
npy_intp *shape = PyArray_DIMS(self);
1284+
npy_intp *strides = PyArray_STRIDES(self);
1285+
char *item = PyArray_DATA(self);
1286+
for (idim = 0; idim < ndim; idim++) {
1287+
npy_intp v = vals[idim];
1288+
if (check_and_adjust_index(&v, shape[idim], idim) < 0) {
1289+
return -1;
1290+
}
1291+
item += v * strides[idim];
12721292
}
1273-
item += v * strides[idim];
1293+
return PyArray_DESCR(self)->f->setitem(op, item, self);
12741294
}
1275-
return PyArray_DESCR(self)->f->setitem(op, item, self);
12761295
}
12771296
PyErr_Clear();
12781297

@@ -1350,6 +1369,7 @@ array_subscript_nice(PyArrayObject *self, PyObject *op)
13501369
{
13511370

13521371
PyArrayObject *mp;
1372+
int ret;
13531373
npy_intp vals[NPY_MAXDIMS];
13541374

13551375
if (PyInt_Check(op) || PyArray_IsScalar(op, Integer) ||
@@ -1364,27 +1384,39 @@ array_subscript_nice(PyArrayObject *self, PyObject *op)
13641384
return array_item_nice(self, (Py_ssize_t) value);
13651385
}
13661386
}
1367-
/* optimization for a tuple of integers */
1368-
if (PyArray_NDIM(self) > 1 &&
1369-
PyTuple_Check(op) &&
1370-
(PyTuple_GET_SIZE(op) == PyArray_NDIM(self)) &&
1371-
(_tuple_of_integers(op, vals, PyArray_NDIM(self)) >= 0)) {
1372-
int idim, ndim = PyArray_NDIM(self);
1373-
npy_intp *shape = PyArray_DIMS(self);
1374-
npy_intp *strides = PyArray_STRIDES(self);
1375-
char *item = PyArray_DATA(self);
1376-
1377-
for (idim = 0; idim < ndim; idim++) {
1378-
npy_intp v = vals[idim];
1379-
if (check_and_adjust_index(&v, shape[idim], idim) < 0) {
1380-
return NULL;
1387+
/*
1388+
* Optimization for a tuple of integers that is the same size as the
1389+
* array's dimension.
1390+
*/
1391+
if (PyArray_NDIM(self) > 1 && _is_full_index(op, self)) {
1392+
ret = _tuple_of_integers(op, vals, PyArray_NDIM(self));
1393+
/* In case an exception occurred (e.g. in PyErr_WarnEx) */
1394+
if (ret < 0) {
1395+
return NULL;
1396+
}
1397+
else if (ret > 0) {
1398+
int idim, ndim = PyArray_NDIM(self);
1399+
npy_intp *shape = PyArray_DIMS(self);
1400+
npy_intp *strides = PyArray_STRIDES(self);
1401+
char *item = PyArray_DATA(self);
1402+
for (idim = 0; idim < ndim; idim++) {
1403+
npy_intp v = vals[idim];
1404+
if (check_and_adjust_index(&v, shape[idim], idim) < 0) {
1405+
return NULL;
1406+
}
1407+
item += v * strides[idim];
13811408
}
1382-
item += v * strides[idim];
1409+
return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self);
13831410
}
1384-
return PyArray_Scalar(item, PyArray_DESCR(self), (PyObject *)self);
13851411
}
13861412
PyErr_Clear();
1387-
1413+
if ((PyNumber_Check(op) || PyArray_IsScalar(op, Number)) &&
1414+
!PyIndex_Check_Or_Unsupported(op)) {
1415+
if (DEPRECATE("non-integer scalar index. In a future numpy "
1416+
"release, this will raise an error.") < 0) {
1417+
return NULL;
1418+
}
1419+
}
13881420
mp = (PyArrayObject *)array_subscript(self, op);
13891421
/*
13901422
* mp could be a scalar if op is not an Int, Scalar, Long or other Index

numpy/core/src/private/npy_pycompat.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
#if (PY_VERSION_HEX < 0x02050000)
2020
#undef PyIndex_Check
2121
#define PyIndex_Check(o) 0
22+
#undef PyIndex_Check_Or_Unsupported
23+
#define PyIndex_Check_Or_Unsupported(o) 1
24+
#else
25+
#define PyIndex_Check_Or_Unsupported(o) PyIndex_Check(o)
2226
#endif
2327

2428
#endif /* _NPY_COMPAT_H_ */

0 commit comments

Comments
 (0)