Skip to content

Commit 84b19cc

Browse files
d-v-bclaude
andcommitted
test: draw zero-length axes in the rectilinear declaration strategies
With #4334 a rectilinear dimension of extent 0 takes any non-empty list of positive edges at creation, the same state 3.2.x wrote and resize(0) produces. The strategies asserted extent > 0 and `chunk_grids` fell back to a regular grid for any empty axis, so no property saw that state. Assisted-by: ClaudeCode:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent f5ef3df commit 84b19cc

2 files changed

Lines changed: 15 additions & 15 deletions

File tree

‎src/zarr/testing/strategies.py‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,12 @@ def arrays(
468468
if isinstance(chunk_grid_meta, RectilinearChunkGridMetadata):
469469
# The per-dimension list form of `chunks=` can only express a grid with
470470
# at least one edge list (bare ints alone declare a regular grid) whose
471-
# edge lists sum exactly to the extent. Other grids are passed as the
471+
# edge lists sum exactly to the extent, or belong to a zero-length axis. Other grids are passed as the
472472
# metadata object, which `create_array` also accepts.
473473
expressible_as_lists = any(
474474
isinstance(dim, tuple) for dim in chunk_grid_meta.chunk_shapes
475475
) and all(
476-
isinstance(dim, int) or sum(dim) == extent
476+
isinstance(dim, int) or extent == 0 or sum(dim) == extent
477477
for dim, extent in zip(chunk_grid_meta.chunk_shapes, nparray.shape, strict=True)
478478
)
479479
if expressible_as_lists and draw(st.booleans(), label="chunks as lists"):
@@ -578,12 +578,19 @@ def chunks_param_from_rectilinear(
578578
def rectilinear_dim_edges(draw: st.DrawFn, *, extent: int) -> list[int]:
579579
"""Explicit chunk edge lengths summing exactly to `extent`.
580580
581+
A zero `extent` has no chunks for edges to cover, and no non-empty list
582+
of positive edges sums to 0; its edges are the chunks the axis grows into
583+
on `append` or `resize`, so any non-empty list of positive edges is drawn.
584+
581585
Two modes: "uneven" cuts the extent at random dividers; "uniform" repeats
582586
one size with an optional remainder, optionally shuffled so equal edges
583587
are not all adjacent. At most 20 chunks per dimension keeps property
584588
tests fast.
585589
"""
586-
assert extent > 0
590+
assert extent >= 0
591+
if extent == 0:
592+
event("rectilinear edges: zero extent")
593+
return draw(st.lists(st.integers(min_value=1, max_value=10), min_size=1, max_size=5))
587594
if extent == 1:
588595
return [1]
589596
if draw(st.booleans(), label="uneven edges"):
@@ -615,7 +622,7 @@ def _rectilinear_step(draw: st.DrawFn, *, extent: int) -> int:
615622
"""A bare-int chunk size for one dimension: a step that repeats to cover
616623
the extent, with the last chunk possibly smaller. A step larger than the
617624
extent (one overhanging chunk) is allowed, as for a regular grid."""
618-
step = draw(st.integers(min_value=math.ceil(extent / 20), max_value=extent + 3))
625+
step = draw(st.integers(min_value=max(1, math.ceil(extent / 20)), max_value=extent + 3))
619626
event("rectilinear dim: bare int" + (", larger than extent" if step > extent else ""))
620627
return step
621628

@@ -625,7 +632,8 @@ def rectilinear_chunks(draw: st.DrawFn, *, shape: tuple[int, ...]) -> list[int |
625632
"""A `chunks=` specification declaring a rectilinear grid over `shape`.
626633
627634
Each dimension is either a bare int (a step size; the last chunk may be
628-
smaller) or an explicit edge list summing to the extent. At least one
635+
smaller) or an explicit edge list summing to the extent (any edges, for a
636+
zero extent). At least one
629637
dimension is an edge list, since bare ints alone declare a regular grid.
630638
Run-length encoding is not part of the `chunks=` syntax; it belongs to
631639
stored metadata, see `rectilinear_chunk_shape_declarations`.
@@ -637,7 +645,6 @@ def rectilinear_chunks(draw: st.DrawFn, *, shape: tuple[int, ...]) -> list[int |
637645
forced_list = draw(st.integers(min_value=0, max_value=len(shape) - 1))
638646
chunks: list[int | list[int]] = []
639647
for i, extent in enumerate(shape):
640-
assert extent > 0
641648
if i != forced_list and draw(st.booleans(), label="bare int"):
642649
chunks.append(_rectilinear_step(draw, extent=extent))
643650
else:
@@ -696,7 +703,6 @@ def rectilinear_chunk_shape_declarations(
696703
declaration: list[_RectilinearDimDeclaration] = []
697704
chunk_shapes: list[int | tuple[int, ...]] = []
698705
for extent in shape:
699-
assert extent > 0
700706
if draw(st.booleans(), label="bare int"):
701707
step = _rectilinear_step(draw, extent=extent)
702708
declaration.append(step)
@@ -742,12 +748,6 @@ def chunk_grids(
742748
743749
This allows property tests to exercise both chunk grid types.
744750
"""
745-
# RectilinearChunkGridMetadata doesn't support zero-sized dimensions,
746-
# so use RegularChunkGridMetadata if any dimension is 0
747-
if any(s == 0 for s in shape):
748-
event("using RegularChunkGridMetadata (zero-sized dimensions)")
749-
return RegularChunkGridMetadata(chunk_shape=draw(chunk_shapes(shape=shape)))
750-
751751
if zarr.config.get("array.rectilinear_chunks") and draw(st.booleans()):
752752
event("using RectilinearChunkGridMetadata")
753753
return draw(rectilinear_chunk_grids(shape=shape))

‎tests/test_properties.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ def test_rectilinear_chunk_grid_declarations(data: st.DataObject) -> None:
604604
steps, edge lists written in full or run-length encoded in any grouping,
605605
edges overhanging the extent — parses to its expanded edges, and the
606606
re-serialized form parses back to the same grid."""
607-
shape = data.draw(npst.array_shapes(max_dims=3, min_side=1, max_side=20), label="shape")
607+
shape = data.draw(npst.array_shapes(max_dims=3, min_side=0, max_side=20), label="shape")
608608
declaration, chunk_shapes = data.draw(
609609
rectilinear_chunk_shape_declarations(shape=shape), label="declaration"
610610
)
@@ -640,7 +640,7 @@ def test_create_array_stores_declared_rectilinear_chunks(data: st.DataObject) ->
640640
arrangement is stored as a rectilinear grid whose `chunk_shapes` are
641641
exactly the specification. Checked on the stored JSON, not only the
642642
in-memory metadata: zarr 3.2.x stored such grids as "regular" (gh-4374)."""
643-
shape = data.draw(npst.array_shapes(max_dims=3, min_side=1, max_side=20), label="shape")
643+
shape = data.draw(npst.array_shapes(max_dims=3, min_side=0, max_side=20), label="shape")
644644
chunks = data.draw(rectilinear_chunks(shape=shape), label="chunks")
645645
arr = zarr.create_array(MemoryStore(), shape=shape, chunks=chunks, dtype="uint8")
646646

0 commit comments

Comments
 (0)