Skip to content

Commit 52b4a94

Browse files
committed
ENH,TST: Add many tests and have boolean finalize at end.
__array_finalize__ is now called in boolean indexing on the result array, so that all numerical values are available.
1 parent 169926f commit 52b4a94

File tree

2 files changed

+137
-44
lines changed

2 files changed

+137
-44
lines changed

numpy/core/src/multiarray/mapping.c

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ prepare_index(PyArrayObject *self, PyObject *index,
523523
"in the future, boolean array-likes will be "
524524
"handled as a boolean array index") < 0) {
525525
Py_DECREF(tmp_arr);
526-
goto failed_building_indices;
526+
goto failed_building_indices;
527527
}
528528
if (PyArray_NDIM(tmp_arr) == 0) {
529529
/*
@@ -562,7 +562,7 @@ prepare_index(PyArrayObject *self, PyObject *index,
562562
"numpy.newaxis (`None`) and integer or boolean "
563563
"arrays are valid indices");
564564
Py_DECREF((PyObject *)tmp_arr);
565-
goto failed_building_indices;
565+
goto failed_building_indices;
566566
}
567567
}
568568
else {
@@ -576,9 +576,9 @@ prepare_index(PyArrayObject *self, PyObject *index,
576576
"non integer (and non boolean) array-likes will "
577577
"not be accepted as indices in the future");
578578
Py_DECREF((PyObject *)tmp_arr);
579-
goto failed_building_indices;
579+
goto failed_building_indices;
580580
}
581-
}
581+
}
582582
}
583583

584584
PyArray_Descr *indtype = PyArray_DescrFromType(NPY_INTP);
@@ -984,8 +984,8 @@ array_boolean_subscript(PyArrayObject *self,
984984
/* Allocate the output of the boolean indexing */
985985
dtype = PyArray_DESCR(self);
986986
Py_INCREF(dtype);
987-
ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self), dtype, 1, &size,
988-
NULL, NULL, 0, (PyObject *)self);
987+
ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, 1, &size,
988+
NULL, NULL, 0, NULL);
989989
if (ret == NULL) {
990990
return NULL;
991991
}
@@ -1074,6 +1074,24 @@ array_boolean_subscript(PyArrayObject *self,
10741074
NPY_AUXDATA_FREE(transferdata);
10751075
}
10761076

1077+
if (!PyArray_CheckExact(self)) {
1078+
PyArrayObject *tmp = ret;
1079+
1080+
ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self), dtype, 1,
1081+
&size, PyArray_STRIDES(ret), PyArray_BYTES(ret),
1082+
0, (PyObject *)self);
1083+
1084+
if (ret == NULL) {
1085+
Py_DECREF(tmp);
1086+
return NULL;
1087+
}
1088+
1089+
if (PyArray_SetBaseObject(ret, tmp) < 0) {
1090+
Py_DECREF(ret);
1091+
return NULL;
1092+
}
1093+
}
1094+
10771095
return ret;
10781096
}
10791097

@@ -1511,10 +1529,11 @@ array_subscript(PyArrayObject *self, PyObject *op)
15111529
Py_DECREF(tmp_arr);
15121530
goto finish;
15131531
}
1514-
/* TODO: Could attempt to steal the data from the old result */
1532+
15151533
if (PyArray_SetBaseObject((PyArrayObject *)result,
15161534
(PyObject *)tmp_arr) < 0) {
1517-
Py_DECREF(tmp_arr);
1535+
Py_DECREF(result);
1536+
result = NULL;
15181537
goto finish;
15191538
}
15201539
}
@@ -1686,25 +1705,20 @@ array_ass_sub(PyArrayObject *self, PyObject *ind, PyObject *op)
16861705

16871706
/* If there is no fancy indexing, we have the array to assign to */
16881707
if (!(index_type & HAS_FANCY)) {
1689-
/*
1690-
* CopyObject handles all weirdness for us, this however also
1691-
* means that other array assignments which convert more strictly
1692-
* do *not* handle all weirdnesses correctly.
1693-
* TODO: To have other assignments handle them correctly, we
1694-
* should copy into a temporary array of the correct shape
1695-
* if it is not an array yet!
1696-
* TODO: We could use PyArray_SETITEM if it is 0-d?
1697-
*/
16981708
if (PyArray_CopyObject(view, op) < 0) {
16991709
goto fail;
17001710
}
17011711
goto success;
17021712
}
17031713

17041714
if (!PyArray_Check(op)) {
1705-
if ((PyDataType_HASFIELDS(descr) || PyDataType_REFCHK(descr))
1706-
&& PySequence_Check(op)) {
1707-
/* Allocate array through MapIter to fill with CopyObject */
1715+
/*
1716+
* If the array is of object converting the values to an array
1717+
* might not be legal even though normal assignment works.
1718+
* So allocate a temporary array of the right size and use the
1719+
* normal assignment to handle this case.
1720+
*/
1721+
if (PyDataType_REFCHK(descr) && PySequence_Check(op)) {
17081722
tmp_arr = NULL;
17091723
}
17101724
else {
@@ -2446,17 +2460,16 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
24462460
* 2. Subspace iteration is necessary, so the extra op is iterated
24472461
* independendly, and the iteration order is fixed at C (could
24482462
* also use Fortran order if the array is Fortran order).
2449-
* In this case the subspace iterator is buffered, so that even
2450-
* the array being iterated *is* buffered!
2463+
* In this case the subspace iterator is not buffered.
24512464
* 3. Subspace iteration is necessary and an extra_op was given.
2452-
* In this case it needs to be transposed!
2465+
* In this case it may need transposing, etc.
24532466
*/
24542467

24552468
/*
24562469
* If we have an extra_op given, need to prepare it.
24572470
* 1. Subclasses might mess with the shape, so need a baseclass
24582471
* 2. Need to make sure the shape is compatible
2459-
* 3. May need to transpose it.
2472+
* 3. May need to remove leading 1s and transpose dimensions.
24602473
*/
24612474
if (extra_op != NULL) {
24622475
if (!PyArray_CheckExact(extra_op)) {
@@ -2471,10 +2484,9 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type,
24712484

24722485
if (PyArray_NDIM(extra_op) > mit->nd) {
24732486
/*
2474-
* Usual assignments allows removal of trailing one dimensions.
2487+
* Usual assignments allows removal of leading one dimensions.
24752488
* (or equivalently adding of one dimensions to the array being
2476-
* assigned to). To implement this, reshape the array. It is a bit
2477-
* of a hack.
2489+
* assigned to). To implement this, reshape the array.
24782490
* This should maybe be done differently, or even not be allowed.
24792491
*/
24802492
PyArrayObject *tmp_arr;

numpy/core/tests/test_indexing.py

Lines changed: 98 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,59 @@ def test_boolean_indexing_twodim(self):
147147
[0, 8, 0]])
148148

149149

150+
def test_reverse_strides_and_subspace_bufferinit(self):
151+
# This tests that the strides are not reversed for simple and
152+
# subspace fancy indexing.
153+
a = np.ones(5)
154+
b = np.zeros(5, dtype=np.intp)[::-1]
155+
c = np.arange(5)[::-1]
156+
157+
a[b] = c
158+
# If the strides are not reversed, the 0 in the arange comes last.
159+
assert_equal(a[0], 0)
160+
161+
# This also tests that the subspace buffer is initiliazed:
162+
a = np.ones((5, 2))
163+
c = np.arange(10).reshape(5, 2)[::-1]
164+
a[b, :] = c
165+
assert_equal(a[0], [0, 1])
166+
167+
168+
def test_too_many_fancy_indices_special_case(self):
169+
# Just documents behaviour, this is a small limitation.
170+
a = np.ones((1,) * 32) # 32 is NPY_MAXDIMS
171+
assert_raises(IndexError, a.__getitem__, (np.array([0]),) * 32)
172+
173+
174+
def test_scalar_array_bool(self):
175+
# Numpy bools can be used as boolean index (python ones as of yet not)
176+
a = np.array(1)
177+
assert_equal(a[np.bool_(True)], a[np.array(True)])
178+
assert_equal(a[np.bool_(False)], a[np.array(False)])
179+
180+
# After deprecating bools as integers:
181+
#a = np.array([0,1,2])
182+
#assert_equal(a[True, :], a[None, :])
183+
#assert_equal(a[:, True], a[:, None])
184+
#
185+
#assert_(not np.may_share_memory(a, a[True, :]))
186+
187+
188+
def test_everything_returns_views(self):
189+
# Before `...` would return a itself.
190+
a = np.arange(5)
191+
192+
assert_(a is not a[()])
193+
assert_(a is not a[...])
194+
assert_(a is not a[:])
195+
196+
150197
class TestBroadcastedAssignments(TestCase):
151198
def assign(self, a, ind, val):
152199
a[ind] = val
153200
return a
154201

202+
155203
def test_prepending_ones(self):
156204
a = np.zeros((3, 2))
157205

@@ -189,27 +237,60 @@ def test_simple_broadcasting_errors(self):
189237
assert_raises(ValueError, assign, a, s_[[0], :], np.zeros((2, 1)))
190238

191239

192-
def test_reverse_strides_and_subspace_bufferinit():
193-
# This tests that the strides are not reversed for simple and
194-
# subspace fancy indexing.
195-
a = np.ones(5)
196-
b = np.zeros(5, dtype=np.intp)[::-1]
197-
c = np.arange(5)[::-1]
240+
def test_index_is_larger(self):
241+
# Simple case of fancy index broadcastin of the index.
242+
a = np.zeros((5, 5))
243+
a[[[0], [1], [2]], [0, 1, 2]] = [2, 3, 4]
244+
245+
assert_((a[:3, :3] == [2, 3, 4]).all())
246+
247+
248+
class TestSubclasses(TestCase):
249+
def test_basic(self):
250+
class SubClass(np.ndarray):
251+
pass
252+
253+
s = np.arange(5).view(SubClass)
254+
assert_(isinstance(s[:3], SubClass))
255+
assert_(s[:3].base is s)
256+
257+
assert_(isinstance(s[[0, 1, 2]], SubClass))
258+
assert_(isinstance(s[s > 0], SubClass))
259+
260+
261+
def test_matrix_fancy(self):
262+
# The matrix class messes with the shape. While this is always
263+
# weird (getitem is not used, it does not have setitem nor knows
264+
# about fancy indexing), this tests gh-3110
265+
m = np.matrix([[1, 2], [3, 4]])
266+
267+
assert_(isinstance(m[[0,1,0], :], np.matrix))
268+
269+
# gh-3110. Note the transpose currently because matrices do *not*
270+
# support dimension fixing for fancy indexing correctly.
271+
x = np.asmatrix(np.arange(50).reshape(5,10))
272+
assert_equal(x[:2, np.array(-1)], x[:2, -1].T)
273+
198274

199-
a[b] = c
200-
# If the strides are not reversed, the 0 in the arange comes last.
201-
assert_equal(a[0], 0)
275+
def test_finalize_gets_full_info(self):
276+
# Array finalize should be called on the filled array.
277+
class SubClass(np.ndarray):
278+
def __array_finalize__(self, old):
279+
self.finalize_status = np.array(self)
280+
self.old = old
202281

203-
# This also tests that the subspace buffer is initiliazed:
204-
a = np.ones((5, 2))
205-
c = np.arange(10).reshape(5, 2)[::-1]
206-
a[b, :] = c
207-
assert_equal(a[0], [0, 1])
282+
s = np.arange(10).view(SubClass)
283+
new_s = s[:3]
284+
assert_array_equal(new_s.finalize_status, new_s)
285+
assert_array_equal(new_s.old, s)
208286

287+
new_s = s[[0,1,2,3]]
288+
assert_array_equal(new_s.finalize_status, new_s)
289+
assert_array_equal(new_s.old, s)
209290

210-
def test_too_many_fancy_indices_special_case():
211-
a = np.ones((1,) * 32) # 32 is NPY_MAXDIMS
212-
assert_raises(IndexError, a.__getitem__, (np.array([0]),) * 32)
291+
new_s = s[s > 0]
292+
assert_array_equal(new_s.finalize_status, new_s)
293+
assert_array_equal(new_s.old, s)
213294

214295

215296
class TestFancyIndexingEquivalence(TestCase):

0 commit comments

Comments
 (0)