Skip to content

Commit d6a12e6

Browse files
committed
test: reuse basic_indices in block_indices strategy
1 parent c6a27a2 commit d6a12e6

1 file changed

Lines changed: 39 additions & 22 deletions

File tree

src/zarr/testing/strategies.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -598,41 +598,58 @@ def block_indices(
598598
"""
599599
Strategy for block-selection indexers over a *regular* chunk grid.
600600
601-
Block indexing addresses whole chunks on the block grid rather than
602-
individual elements. It only supports integers and step-1 slices over the
603-
grid (strided block slices are rejected), so neither newaxis, ellipsis, nor
604-
a step is generated here. The array-space translation below assumes a
605-
regular (uniform) chunk grid, so ``shape`` must be evenly tiled by
606-
``chunks`` up to a possibly-smaller last chunk per dimension. Every
607-
dimension must have at least one chunk (``size >= 1``).
601+
Block indexing is basic indexing applied to the block grid (the grid of
602+
chunks), so each axis is drawn with ``basic_indices`` over that axis's chunk
603+
count -- mirroring how ``orthogonal_indices`` reuses ``basic_indices`` per
604+
axis. Block indexing only supports integers and step-1 slices whose start
605+
references an existing chunk, so strided slices and slices starting at the
606+
grid edge are filtered out. The array-space translation assumes a regular
607+
(uniform) chunk grid, so ``shape`` must be evenly tiled by ``chunks`` up to a
608+
possibly-smaller last chunk per dimension, and every dimension must have at
609+
least one chunk (``size >= 1``).
608610
609611
Returns
610612
-------
611613
block_indexer
612-
A tuple of ints / step-1 slices addressing whole chunks, suitable for
613-
``Array.blocks`` / ``Array.get_block_selection`` / ``set_block_selection``.
614+
A per-axis tuple of ints / step-1 slices addressing whole chunks,
615+
suitable for ``Array.blocks`` / ``get_block_selection`` / ``set_block_selection``.
614616
array_indexer
615617
The equivalent array-space selection (a tuple of slices) for indexing
616618
the corresponding numpy array, used as the comparison oracle.
617619
"""
618620
grid_shape = tuple(-(-s // c) for s, c in zip(shape, chunks, strict=True)) # ceil division
621+
622+
def supported(nchunks: int) -> Callable[[tuple[Any, ...]], bool]:
623+
# Block indexing only accepts step-1 slices whose start references an
624+
# existing chunk (a slice starting at nchunks raises, unlike numpy).
625+
def predicate(value: tuple[Any, ...]) -> bool:
626+
dim_sel = value[0]
627+
if isinstance(dim_sel, slice):
628+
if dim_sel.step not in (None, 1):
629+
return False
630+
start = dim_sel.start or 0
631+
return 0 <= (start + nchunks if start < 0 else start) < nchunks
632+
return True
633+
634+
return predicate
635+
619636
block_indexer: list[int | slice] = []
620637
array_indexer: list[slice] = []
621638
for size, chunk, nchunks in zip(shape, chunks, grid_shape, strict=True):
622-
if draw(st.booleans()):
623-
# a single block, sometimes addressed from the end with a negative index
624-
block = draw(st.integers(min_value=-nchunks, max_value=nchunks - 1))
625-
block_indexer.append(block)
626-
start = (block % nchunks) * chunk
627-
array_indexer.append(slice(start, min(start + chunk, size)))
639+
(dim_sel,) = draw(
640+
basic_indices(min_dims=1, shape=(nchunks,), allow_ellipsis=False)
641+
# normalize bare ints / slices to a 1-tuple, skip the empty tuple
642+
.map(lambda x: (x,) if not isinstance(x, tuple) else x)
643+
.filter(bool)
644+
.filter(supported(nchunks))
645+
)
646+
block_indexer.append(dim_sel)
647+
if isinstance(dim_sel, slice):
648+
start, stop, _ = dim_sel.indices(nchunks)
649+
array_indexer.append(slice(start * chunk, min(stop * chunk, size)))
628650
else:
629-
# a contiguous run of whole blocks (possibly empty). The start must
630-
# reference an existing chunk: block indexing rejects a slice that
631-
# starts at nchunks, unlike numpy which treats arr[len:len] as empty.
632-
start_block = draw(st.integers(min_value=0, max_value=nchunks - 1))
633-
stop_block = draw(st.integers(min_value=start_block, max_value=nchunks))
634-
block_indexer.append(slice(start_block, stop_block))
635-
array_indexer.append(slice(start_block * chunk, min(stop_block * chunk, size)))
651+
block = dim_sel % nchunks
652+
array_indexer.append(slice(block * chunk, min((block + 1) * chunk, size)))
636653
return tuple(block_indexer), tuple(array_indexer)
637654

638655

0 commit comments

Comments
 (0)