Skip to content

Commit dd7d5db

Browse files
authored
Merge pull request #122 from static-frame/121/block-index-neg-indices
Sequence iters to handle negative numbers
2 parents 2b66ea7 + 2b0d3d9 commit dd7d5db

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

src/_arraykit.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4183,8 +4183,8 @@ typedef struct BlockIndexObject {
41834183
} BlockIndexObject;
41844184

41854185

4186-
// Returns a new reference to tuple. Returns NULL on error.
4187-
static PyObject*
4186+
// Returns a new reference to tuple. Returns NULL on error. Python already wraps negative numbers up to negative length when used in the sequence slot
4187+
static inline PyObject*
41884188
AK_BI_item(BlockIndexObject* self, Py_ssize_t i) {
41894189
if (!((size_t)i < (size_t)self->bir_count)) {
41904190
PyErr_SetString(PyExc_IndexError, "index out of range");
@@ -4194,6 +4194,16 @@ AK_BI_item(BlockIndexObject* self, Py_ssize_t i) {
41944194
return Py_BuildValue("nn", biri->block, biri->column); // maybe NULL
41954195
}
41964196

4197+
// Returns a new reference to tuple. Returns NULL on error. Supports negative numbers up to negative length.
4198+
static inline PyObject*
4199+
AK_BI_item_wraps(BlockIndexObject* self, Py_ssize_t i)
4200+
{
4201+
if (i < 0) {
4202+
i = self->bir_count + i;
4203+
}
4204+
return AK_BI_item(self, i);
4205+
}
4206+
41974207
//------------------------------------------------------------------------------
41984208
// BI Iterator
41994209
static PyTypeObject BIIterType;
@@ -4378,7 +4388,7 @@ BIIterSeq_iternext(BIIterSeqObject *self) {
43784388
return NULL;
43794389
}
43804390
}
4381-
return AK_BI_item(self->bi, t); // return new ref
4391+
return AK_BI_item_wraps(self->bi, t); // return new ref
43824392
}
43834393

43844394
static PyObject *
@@ -4557,7 +4567,6 @@ static PyTypeObject BIIterBooleanType = {
45574567
.tp_name = "arraykit.BlockIndexIteratorBoolean",
45584568
};
45594569

4560-
45614570
//------------------------------------------------------------------------------
45624571

45634572
// NOTE: this constructor returns one of three different PyObject types. We do this to consolidate error reporting and type checks.
@@ -4628,7 +4637,6 @@ BIIterSelector_new(BlockIndexObject *bi,
46284637
pos += (step * (len - 1));
46294638
step *= -1;
46304639
}
4631-
// AK_DEBUG_MSG_OBJ("resolved slice", Py_BuildValue("nnnn", pos, stop, step, len));
46324640
}
46334641
else if (PyList_CheckExact(selector)) {
46344642
if (kind == BIIS_UNKNOWN) {
@@ -4694,8 +4702,6 @@ BIIterSelector_new(BlockIndexObject *bi,
46944702
return NULL;
46954703
}
46964704

4697-
4698-
46994705
//------------------------------------------------------------------------------
47004706

47014707
// Returns 0 on succes, -1 on error.

test/test_block_index.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,4 +528,27 @@ def test_block_index_iter_select_boolean_c(self) -> None:
528528
)
529529
self.assertEqual(list(bi1.iter_select(np.full(len(bi1), True))),
530530
[(0, 0), (0, 1), (1, 0)]
531-
)
531+
)
532+
533+
#---------------------------------------------------------------------------
534+
def test_block_index_iter_select_sequence_a(self) -> None:
535+
bi1 = BlockIndex()
536+
bi1.register(np.arange(4).reshape(2,2))
537+
bi1.register(np.arange(2))
538+
bi1.register(np.arange(10).reshape(2,5))
539+
540+
self.assertEqual(list(bi1.iter_select([0, -1, -2, -8])),
541+
[(0, 0), (2, 4), (2, 3), (0, 0)]
542+
)
543+
self.assertEqual(list(bi1.iter_select(np.array([0, -1, -2, -8]))),
544+
[(0, 0), (2, 4), (2, 3), (0, 0)]
545+
)
546+
547+
def test_block_index_iter_select_sequence_b(self) -> None:
548+
bi1 = BlockIndex()
549+
bi1.register(np.arange(4).reshape(2,2))
550+
bi1.register(np.arange(2))
551+
bi1.register(np.arange(10).reshape(2,5))
552+
553+
with self.assertRaises(IndexError):
554+
_ = list(bi1.iter_select([-9]))

0 commit comments

Comments
 (0)