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

Skip to content

Commit 5c3f9d3

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

File tree

5 files changed

+24
-130
lines changed

5 files changed

+24
-130
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Expired deprecation of multidimensional indexing with non-tuple values
2+
----------------------------------------------------------------------
3+
4+
Multidimensional indexing with anything but a tuple was
5+
deprecated in NumPy 1.15.
6+
7+
Previously, code such as ``arr[ind]`` where ``ind = [[0, 1], [0, 1]]``
8+
produced a ``FutureWarning`` and was interpreted as a multidimensional
9+
index (i.e., ``arr[tuple(ind)]``). Now this example is treated like an
10+
array index over a single dimension (``arr[array(ind)]``).

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: 8 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -197,21 +197,8 @@ unpack_scalar(PyObject *index, PyObject **result, npy_intp NPY_UNUSED(result_n))
197197
/**
198198
* Turn an index argument into a c-array of `PyObject *`s, one for each index.
199199
*
200-
* When a scalar is passed, this is written directly to the buffer. When a
201-
* tuple is passed, the tuple elements are unpacked into the buffer.
202-
*
203-
* When some other sequence is passed, this implements the following section
204-
* from the advanced indexing docs to decide whether to unpack or just write
205-
* one element:
206-
*
207-
* > In order to remain backward compatible with a common usage in Numeric,
208-
* > basic slicing is also initiated if the selection object is any non-ndarray
209-
* > sequence (such as a list) containing slice objects, the Ellipsis object,
210-
* > or the newaxis object, but not for integer arrays or other embedded
211-
* > sequences.
212-
*
213-
* It might be worth deprecating this behaviour (gh-4434), in which case the
214-
* entire function should become a simple check of PyTuple_Check.
200+
* When a tuple is passed, the tuple elements are unpacked into the buffer.
201+
* Anything else is handled by unpack_scalar().
215202
*
216203
* @param index The index object, which may or may not be a tuple. This is
217204
* a borrowed reference.
@@ -228,8 +215,11 @@ unpack_scalar(PyObject *index, PyObject **result, npy_intp NPY_UNUSED(result_n))
228215
NPY_NO_EXPORT npy_intp
229216
unpack_indices(PyObject *index, PyObject **result, npy_intp result_n)
230217
{
231-
npy_intp n, i;
232-
npy_bool commit_to_unpack;
218+
npy_intp n;
219+
220+
/* It is likely that the logic here can be simplified. See the discussion on
221+
* https://github.com/numpy/numpy/pull/21029
222+
*/
233223

234224
/* Fast route for passing a tuple */
235225
if (PyTuple_CheckExact(index)) {
@@ -262,95 +252,7 @@ unpack_indices(PyObject *index, PyObject **result, npy_intp result_n)
262252
return n;
263253
}
264254

265-
/*
266-
* 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.
298-
*/
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]`. In 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;
255+
return unpack_scalar(index, result, result_n);
354256
}
355257

356258
/**

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