From b5b92af99d0fa8d3f7d75451f2855a60e907069d Mon Sep 17 00:00:00 2001 From: brokkoli71 Date: Wed, 23 Oct 2024 17:19:52 +0200 Subject: [PATCH 1/4] support zero-sized chunks --- src/zarr/core/indexing.py | 4 +++- tests/test_indexing.py | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/zarr/core/indexing.py b/src/zarr/core/indexing.py index f1d5fd16d..1873d5c83 100644 --- a/src/zarr/core/indexing.py +++ b/src/zarr/core/indexing.py @@ -94,6 +94,8 @@ def __iter__(self) -> Iterator[ChunkProjection]: ... def ceildiv(a: float, b: float) -> int: + if a == 0: + return 0 return math.ceil(a / b) @@ -374,7 +376,7 @@ def __init__(self, dim_sel: slice, dim_len: int, dim_chunk_len: int) -> None: def __iter__(self) -> Iterator[ChunkDimProjection]: # figure out the range of chunks we need to visit - dim_chunk_ix_from = self.start // self.dim_chunk_len + dim_chunk_ix_from = 0 if self.start == 0 else self.start // self.dim_chunk_len dim_chunk_ix_to = ceildiv(self.stop, self.dim_chunk_len) # iterate over chunks in range diff --git a/tests/test_indexing.py b/tests/test_indexing.py index b3a199068..8b3693569 100644 --- a/tests/test_indexing.py +++ b/tests/test_indexing.py @@ -11,6 +11,7 @@ from numpy.testing import assert_array_equal import zarr +from zarr import Array from zarr.core.buffer import BufferPrototype, default_buffer_prototype from zarr.core.indexing import ( BasicSelection, @@ -1927,3 +1928,11 @@ def test_indexing_with_zarr_array(store: StorePath) -> None: assert_array_equal(a[ii], za[zii]) assert_array_equal(a[ii], za.oindex[zii]) + + +@pytest.mark.parametrize("store", ["local", "memory"], indirect=["store"]) +@pytest.mark.parametrize("shape", [(0, 2, 3), (0), (3, 0)]) +def test_zero_sized_chunks(store: StorePath, shape: list[int]) -> None: + z = Array.create(store=store, shape=shape, chunk_shape=shape, zarr_format=3, dtype="f8") + z[...] = 42 + assert_array_equal(z[...], np.zeros(shape, dtype="f8")) From 3622d1bb3f7cf9530541687993943241e30826cf Mon Sep 17 00:00:00 2001 From: brokkoli71 Date: Wed, 23 Oct 2024 17:31:21 +0200 Subject: [PATCH 2/4] fix imports --- tests/test_indexing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_indexing.py b/tests/test_indexing.py index 8b3693569..2c51f3da3 100644 --- a/tests/test_indexing.py +++ b/tests/test_indexing.py @@ -32,7 +32,6 @@ if TYPE_CHECKING: from collections.abc import AsyncGenerator - from zarr.core.array import Array from zarr.core.buffer.core import Buffer from zarr.core.common import ChunkCoords From 18a53cbc63ee22109bb76e88b96b892895a44653 Mon Sep 17 00:00:00 2001 From: brokkoli71 Date: Wed, 23 Oct 2024 20:36:04 +0200 Subject: [PATCH 3/4] add min_side=0 to testing strategies --- src/zarr/testing/strategies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zarr/testing/strategies.py b/src/zarr/testing/strategies.py index 2c17fbf79..52c5ac559 100644 --- a/src/zarr/testing/strategies.py +++ b/src/zarr/testing/strategies.py @@ -65,7 +65,7 @@ def v2_dtypes() -> st.SearchStrategy[np.dtype]: stores = st.builds(MemoryStore, st.just({}), mode=st.just("w")) compressors = st.sampled_from([None, "default"]) zarr_formats: st.SearchStrategy[Literal[2, 3]] = st.sampled_from([2, 3]) -array_shapes = npst.array_shapes(max_dims=4) +array_shapes = npst.array_shapes(max_dims=4, min_side=0) @st.composite # type: ignore[misc] From a1eea782c94ece0bbc53d897cde7adf54cd927d8 Mon Sep 17 00:00:00 2001 From: Deepak Cherian Date: Wed, 23 Oct 2024 13:14:16 -0600 Subject: [PATCH 4/4] fix property tests --- src/zarr/testing/strategies.py | 13 ++++++++++--- tests/test_properties.py | 6 ++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/zarr/testing/strategies.py b/src/zarr/testing/strategies.py index 52c5ac559..c82e168cf 100644 --- a/src/zarr/testing/strategies.py +++ b/src/zarr/testing/strategies.py @@ -85,7 +85,7 @@ def numpy_arrays( @st.composite # type: ignore[misc] def np_array_and_chunks( draw: st.DrawFn, *, arrays: st.SearchStrategy[np.ndarray] = numpy_arrays -) -> tuple[np.ndarray, tuple[int]]: # type: ignore[type-arg] +) -> tuple[np.ndarray, tuple[int, ...]]: # type: ignore[type-arg] """A hypothesis strategy to generate small sized random arrays. Returns: a tuple of the array and a suitable random chunking for it. @@ -93,9 +93,16 @@ def np_array_and_chunks( array = draw(arrays) # We want this strategy to shrink towards arrays with smaller number of chunks # 1. st.integers() shrinks towards smaller values. So we use that to generate number of chunks - numchunks = draw(st.tuples(*[st.integers(min_value=1, max_value=size) for size in array.shape])) + numchunks = draw( + st.tuples( + *[st.integers(min_value=0 if size == 0 else 1, max_value=size) for size in array.shape] + ) + ) # 2. and now generate the chunks tuple - chunks = tuple(size // nchunks for size, nchunks in zip(array.shape, numchunks, strict=True)) + chunks = tuple( + size // nchunks if nchunks > 0 else 0 + for size, nchunks in zip(array.shape, numchunks, strict=True) + ) return (array, chunks) diff --git a/tests/test_properties.py b/tests/test_properties.py index 380a4d851..f70753ceb 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -6,7 +6,7 @@ import hypothesis.extra.numpy as npst # noqa: E402 import hypothesis.strategies as st # noqa: E402 -from hypothesis import given # noqa: E402 +from hypothesis import assume, given # noqa: E402 from zarr.testing.strategies import arrays, basic_indices, numpy_arrays, zarr_formats # noqa: E402 @@ -35,11 +35,13 @@ def test_basic_indexing(data: st.DataObject) -> None: @given(data=st.data()) def test_vindex(data: st.DataObject) -> None: zarray = data.draw(arrays()) + # integer_array_indices can't handle 0-size dimensions. + assume(all(s > 0 for s in zarray.shape)) nparray = zarray[:] indexer = data.draw( npst.integer_array_indices( - shape=nparray.shape, result_shape=npst.array_shapes(max_dims=None) + shape=nparray.shape, result_shape=npst.array_shapes(min_side=1, max_dims=None) ) ) actual = zarray.vindex[indexer]