Skip to content

Commit 3dd6121

Browse files
committed
guard against storage_options["chunks"]="" + Change ValueError
1 parent 2450bd4 commit 3dd6121

2 files changed

Lines changed: 57 additions & 4 deletions

File tree

src/spatialdata/_io/io_raster.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,16 @@
4040

4141

4242
def _is_flat_int_sequence(value: object) -> TypeGuard[Sequence[int]]:
43+
if isinstance(value, str | bytes):
44+
return False
4345
if not isinstance(value, Sequence):
4446
return False
4547
return all(isinstance(v, int) for v in value)
4648

4749

4850
def _is_dask_chunk_grid(value: object) -> TypeGuard[Sequence[Sequence[int]]]:
51+
if isinstance(value, str | bytes):
52+
return False
4953
if not isinstance(value, Sequence):
5054
return False
5155
return len(value) > 0 and all(_is_flat_int_sequence(axis_chunks) for axis_chunks in value)
@@ -128,8 +132,10 @@ def _normalize_explicit_chunks(chunks: object) -> tuple[int, ...] | int:
128132
normalized = _chunks_to_zarr_chunks(chunks)
129133
if normalized is None:
130134
raise ValueError(
131-
"storage_options['chunks'] must be a Zarr chunk shape or a regular Dask chunk grid. "
132-
"Irregular Dask chunk grids must be rechunked before writing or omitted."
135+
'storage_options["chunks"] must resolve to a Zarr chunk shape or a regular Dask chunk grid. '
136+
"The current raster has irregular Dask chunks, which cannot be written to Zarr. "
137+
"To fix this, rechunk before writing, for example by passing regular chunks=... "
138+
"to Image2DModel.parse(...) / Labels2DModel.parse(...)."
133139
)
134140
return normalized
135141

tests/io/test_readwrite.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ def test_write_irregular_dask_chunks_without_explicit_storage_options(tmp_path:
656656

657657
with pytest.raises(
658658
ValueError,
659-
match="storage_options\\['chunks'\\] must be a Zarr chunk shape or a regular Dask chunk grid",
659+
match='storage_options\\["chunks"\\] must resolve to a Zarr chunk shape or a regular Dask chunk grid',
660660
):
661661
sdata.write(tmp_path / "data.zarr")
662662

@@ -678,11 +678,58 @@ def test_write_image_rejects_explicit_irregular_dask_chunk_grid(tmp_path: Path)
678678

679679
with pytest.raises(
680680
ValueError,
681-
match="storage_options\\['chunks'\\] must be a Zarr chunk shape or a regular Dask chunk grid",
681+
match='storage_options\\["chunks"\\] must resolve to a Zarr chunk shape or a regular Dask chunk grid',
682682
):
683683
write_image(image, group, "image", storage_options={"chunks": image.data.chunks})
684684

685685

686+
def test_write_image_normalizes_explicit_zarr_chunk_grid(tmp_path: Path) -> None:
687+
data = da.from_array(RNG.random((3, 800, 1000)), chunks=((3,), (300, 200, 300), (512, 488)))
688+
image = Image2DModel.parse(data, dims=("c", "y", "x"))
689+
group = zarr.open_group(tmp_path / "image.zarr", mode="w")
690+
691+
zarr_chunks = (3, 100, 512) # ome zarr rechunks when writing
692+
write_image(image, group, "image", storage_options={"chunks": zarr_chunks})
693+
694+
assert group["s0"].chunks == (3, 100, 512)
695+
696+
697+
def test_write_image_rejects_string(tmp_path: Path) -> None:
698+
data = da.from_array(RNG.random((3, 800, 1000)), chunks=((3,), (300, 300, 200), (512, 488)))
699+
image = Image2DModel.parse(data, dims=("c", "y", "x"))
700+
group = zarr.open_group(tmp_path / "image.zarr", mode="w")
701+
702+
with pytest.raises(
703+
ValueError,
704+
match='storage_options\\["chunks"\\] must resolve to a Zarr chunk shape or a regular Dask chunk grid',
705+
):
706+
write_image(image, group, "image", storage_options={"chunks": "auto"})
707+
708+
709+
def test_write_image_rejects_empty_string(tmp_path: Path) -> None:
710+
data = da.from_array(RNG.random((3, 800, 1000)), chunks=((3,), (300, 300, 200), (512, 488)))
711+
image = Image2DModel.parse(data, dims=("c", "y", "x"))
712+
group = zarr.open_group(tmp_path / "image.zarr", mode="w")
713+
714+
with pytest.raises(
715+
ValueError,
716+
match='storage_options\\["chunks"\\] must resolve to a Zarr chunk shape or a regular Dask chunk grid',
717+
):
718+
write_image(image, group, "image", storage_options={"chunks": ""})
719+
720+
721+
def test_write_image_rejects_byte_string(tmp_path: Path) -> None:
722+
data = da.from_array(RNG.random((3, 800, 1000)), chunks=((3,), (300, 300, 200), (512, 488)))
723+
image = Image2DModel.parse(data, dims=("c", "y", "x"))
724+
group = zarr.open_group(tmp_path / "image.zarr", mode="w")
725+
726+
with pytest.raises(
727+
ValueError,
728+
match='storage_options\\["chunks"\\] must resolve to a Zarr chunk shape or a regular Dask chunk grid',
729+
):
730+
write_image(image, group, "image", storage_options={"chunks": b"auto"})
731+
732+
686733
def test_single_scale_image_roundtrip_stays_dataarray(tmp_path: Path) -> None:
687734
image = Image2DModel.parse(RNG.random((3, 64, 64)), dims=("c", "y", "x"))
688735
sdata = SpatialData(images={"image": image})

0 commit comments

Comments
 (0)