8000 DEP: Remove support for non-tuple nd-indices. · hawkinsp/numpy@38d60ed · GitHub
[go: up one dir, main page]

Skip to content

Commit 38d60ed

Browse files
committed
DEP: Remove support for non-tuple nd-indices.
This behavior has been deprecated since NumPy 1.15 (numpy#9686).
1 parent 06ac508 commit 38d60ed

File tree

5 files changed

+18
-112
lines changed

5 files changed

+18
-112
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Multidimensional indexing with non-tuple values is no longer permitted
2+
----------------------------------------------------------------------
3+
4+
Multidimensional indexing with anything but a tuple was
5+
deprecated in NumPy 1.15. Code such as ``ind = [slice(None), 0]``,
6+
``arr[[slice(None), 0]]`` should be changed to ``arr[tuple(ind)]``. This is
7+
necessary to avoid ambiguity in expressions such as ``arr[[[0, 1], [0, 1]]]``
8+
which was previously interpreted as ``arr[array([0, 1]), array([0, 1])]``,
9+
but is now interpreted as ``arr[array([[0, 1], [0, 1]])]``.

doc/source/user/basics.indexing.rst

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,6 @@ integer, or a tuple of slice objects and integers. :py:data:`Ellipsis`
104104
and :const:`newaxis` objects can be interspersed with these as
105105
well.
106106

107-
.. deprecated:: 1.15.0
108-
109-
In order to remain backward compatible with a common usage in
110-
Numeric, basic slicing is also initiated if the selection object is
111-
any non-ndarray and non-tuple sequence (such as a :class:`list`) containing
112-
:class:`slice` objects, the :py:data:`Ellipsis` object, or the :const:`newaxis`
113-
object, but not for integer arrays or other embedded sequences.
114-
115107
.. index::
116108
triple: ndarray; special methods; getitem
117109
triple: ndarray; special methods; setitem

numpy/core/src/multiarray/mapping.c

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,7 @@ unpack_scalar(PyObject *index, PyObject **result, npy_intp NPY_UNUSED(result_n))
228228
NPY_NO_EXPORT npy_intp
229229
unpack_indices(PyObject *index, PyObject **result, npy_intp result_n)
230230
{
231-
npy_intp n, i;
232-
npy_bool commit_to_unpack;
231+
npy_intp n;
233232

234233
/* Fast route for passing a tuple */
235234
if (PyTuple_CheckExact(index)) {
@@ -264,93 +263,9 @@ unpack_indices(PyObject *index, PyObject **result, npy_intp result_n)
264263

265264
/*
266265
* At this point, we're left with a non-tuple, non-array, sequence:
267-
* typically, a list. We use some somewhat-arbitrary heuristics from here
268-
* onwards to decided whether to treat that list as a single index, or a
269-
* list of indices.
270-
*/
271-
272-
/* if len fails, treat like a scalar */
273-
n = PySequence_Size(index);
274-
if (n < 0) {
275-
PyErr_Clear();
276-
return unpack_scalar(index, result, result_n);
277-
}
278-
279-
/*
280-
* Backwards compatibility only takes effect for short sequences - otherwise
281-
* we treat it like any other scalar.
282-
*
283-
* Sequences < NPY_MAXDIMS with any slice objects
284-
* or newaxis, Ellipsis or other arrays or sequences
285-
* embedded, are considered equivalent to an indexing
286-
* tuple. (`a[[[1,2], [3,4]]] == a[[1,2], [3,4]]`)
287-
*/
288-
if (n >= NPY_MAXDIMS) {
289-
return unpack_scalar(index, result, result_n);
290-
}
291-
292-
/* In case we change result_n elsewhere */
293-
assert(n <= result_n);
294-
295-
/*
296-
* Some other type of short sequence - assume we should unpack it like a
297-
* tuple, and then decide whether that was actually necessary.
266+
* typically, a list. We always treat these as single indices.
298267
*/
299-
commit_to_unpack = 0;
300-
for (i = 0; i < n; i++) {
301-
PyObject *tmp_obj = result[i] = PySequence_GetItem(index, i);
302-
303-
if (commit_to_unpack) {
304-
/* propagate errors */
305-
if (tmp_obj == NULL) {
306-
goto fail;
307-
}
308-
}
309-
else {
310-
/*
311-
* if getitem fails (unusual) before we've committed, then stop
312-
* unpacking
313-
*/
314-
if (tmp_obj == NULL) {
315-
PyErr_Clear();
316-
break;
317-
}
318-
319-
/* decide if we should treat this sequence like a tuple */
320-
if (PyArray_Check(tmp_obj)
321-
|| PySequence_Check(tmp_obj)
322-
|| PySlice_Check(tmp_obj)
323-
|| tmp_obj == Py_Ellipsis
324-
|| tmp_obj == Py_None) {
325-
if (DEPRECATE_FUTUREWARNING(
326-
"Using a non-tuple sequence for multidimensional "
327-
"indexing is deprecated; use `arr[tuple(seq)]` "
328-
"instead of `arr[seq]`. I 9E81 n the future this will be "
329-
"interpreted as an array index, `arr[np.array(seq)]`, "
330-
"which will result either in an error or a different "
331-
"result.") < 0) {
332-
i++; /* since loop update doesn't run */
333-
goto fail;
334-
}
335-
commit_to_unpack = 1;
336-
}
337-
}
338-
}
339-
340-
/* unpacking was the right thing to do, and we already did it */
341-
if (commit_to_unpack) {
342-
return n;
343-
}
344-
/* got to the end, never found an indication that we should have unpacked */
345-
else {
346-
/* we partially filled result, so empty it first */
347-
multi_DECREF(result, i);
348-
return unpack_scalar(index, result, result_n);
349-
}
350-
351-
fail:
352-
multi_DECREF(result, i);
353-
return -1;
268+
return unpack_scalar(index, result, result_n);
354269
}
355270

356271
/**

numpy/core/tests/test_deprecations.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,22 +138,6 @@ class _VisibleDeprecationTestCase(_DeprecationTestCase):
138138
warning_cls = np.VisibleDeprecationWarning
139139

140140

141-
class TestNonTupleNDIndexDeprecation:
142-
def test_basic(self):
143-
a = np.zeros((5, 5))
144-
with warnings.catch_warnings():
145-
warnings.filterwarnings('always')
146-
assert_warns(FutureWarning, a.__getitem__, [[0, 1], [0, 1]])
147-
assert_warns(FutureWarning, a.__getitem__, [slice(None)])
148-
149-
warnings.filterwarnings('error')
150-
assert_raises(FutureWarning, a.__getitem__, [[0, 1], [0, 1]])
151-
assert_raises(FutureWarning, a.__getitem__, [slice(None)])
152-
153-
# a a[[0, 1]] always was advanced indexing, so no error/warning
154-
a[[0, 1]]
155-
156-
157141
class TestComparisonDeprecations(_DeprecationTestCase):
158142
"""This tests the deprecation, for non-element-wise comparison logic.
159143
This used to mean that when an error occurred during element-wise comparison

numpy/core/tests/test_indexing.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,12 @@ def func(arr):
587587

588588
assert arr.dtype is dt
589589

590+
def test_nontuple_ndindex(self):
591+
a = np.arange(25).reshape((5, 5))
592+
assert_equal(a[[0, 1]], np.array([a[0], a[1]]))
593+
assert_equal(a[[0, 1], [0, 1]], np.array([0, 6]))
594+
assert_raises(IndexError, a.__getitem__, [slice(None)])
595+
590596

591597
class TestFieldIndexing:
592598
def test_scalar_return_type(self):

0 commit comments

Comments
 (0)
0