Skip to content

Commit 6436db6

Browse files
committed
Create and use resolve_chunks functions
1 parent c4f7cf4 commit 6436db6

4 files changed

Lines changed: 57 additions & 56 deletions

File tree

src/zarr/core/array.py

Lines changed: 26 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
RectilinearChunkGrid,
123123
RegularChunkGrid,
124124
parse_node_type_array,
125+
resolve_chunks,
125126
)
126127
from zarr.core.sync import sync
127128
from zarr.errors import (
@@ -676,28 +677,9 @@ async def _create(
676677
if chunks is not None and chunk_shape is not None:
677678
raise ValueError("Only one of chunk_shape or chunks can be provided.")
678679

679-
# detect rectilinear (dask-style) chunks before normalize_chunks flattens them
680680
from zarr.core.chunk_grids import _is_rectilinear_chunks
681681

682682
_raw_chunks = chunks if chunks is not None else chunk_shape
683-
_rectilinear_chunk_grid: ChunkGridMetadata | None = None
684-
_chunks: tuple[int, ...] | None = None
685-
if _is_rectilinear_chunks(_raw_chunks):
686-
from zarr.core.metadata.v3 import (
687-
RectilinearChunkGrid as RectilinearChunkGridMeta,
688-
)
689-
690-
_rectilinear_chunk_grid = RectilinearChunkGridMeta(
691-
chunk_shapes=tuple(tuple(c) for c in _raw_chunks)
692-
)
693-
else:
694-
item_size = 1
695-
if isinstance(dtype_parsed, HasItemSize):
696-
item_size = dtype_parsed.item_size
697-
if chunks:
698-
_chunks = normalize_chunks(chunks, shape, item_size)
699-
else:
700-
_chunks = normalize_chunks(chunk_shape, shape, item_size)
701683

702684
config_parsed = parse_array_config(config)
703685

@@ -719,19 +701,22 @@ async def _create(
719701
if order is not None:
720702
_warn_order_kwarg()
721703

704+
item_size = 1
705+
if isinstance(dtype_parsed, HasItemSize):
706+
item_size = dtype_parsed.item_size
707+
chunk_grid = resolve_chunks(_raw_chunks, shape, item_size)
722708
result = await cls._create_v3(
723709
store_path,
724710
shape=shape,
725711
dtype=dtype_parsed,
726-
chunk_shape=_chunks,
727712
fill_value=fill_value,
728713
chunk_key_encoding=chunk_key_encoding,
729714
codecs=codecs,
730715
dimension_names=dimension_names,
731716
attributes=attributes,
732717
overwrite=overwrite,
733718
config=config_parsed,
734-
chunk_grid=_rectilinear_chunk_grid,
719+
chunk_grid=chunk_grid,
735720
)
736721
elif zarr_format == 2:
737722
if codecs is not None:
@@ -744,9 +729,16 @@ async def _create(
744729
)
745730
if dimension_names is not None:
746731
raise ValueError("dimension_names cannot be used for arrays with zarr_format 2.")
747-
if _rectilinear_chunk_grid is not None:
732+
if _is_rectilinear_chunks(_raw_chunks):
748733
raise ValueError("Zarr format 2 does not support rectilinear chunk grids.")
749-
assert _chunks is not None
734+
735+
item_size = 1
736+
if isinstance(dtype_parsed, HasItemSize):
737+
item_size = dtype_parsed.item_size
738+
if chunks:
739+
_chunks = normalize_chunks(chunks, shape, item_size)
740+
else:
741+
_chunks = normalize_chunks(chunk_shape, shape, item_size)
750742

751743
if order is None:
752744
order_parsed = config_parsed.order
@@ -781,23 +773,14 @@ async def _create(
781773
def _create_metadata_v3(
782774
shape: ShapeLike,
783775
dtype: ZDType[TBaseDType, TBaseScalar],
784-
chunk_shape: tuple[int, ...] | None = None,
776+
chunk_grid: ChunkGridMetadata,
785777
fill_value: Any | None = DEFAULT_FILL_VALUE,
786778
chunk_key_encoding: ChunkKeyEncodingLike | None = None,
787779
codecs: Iterable[Codec | dict[str, JSON]] | None = None,
788780
dimension_names: DimensionNamesLike = None,
789781
attributes: dict[str, JSON] | None = None,
790-
chunk_grid: ChunkGridMetadata | None = None,
791782
) -> ArrayV3Metadata:
792-
"""
793-
Create an instance of ArrayV3Metadata.
794-
795-
Exactly one of ``chunk_shape`` or ``chunk_grid`` must be provided.
796-
"""
797-
if chunk_shape is not None and chunk_grid is not None:
798-
raise ValueError("Only one of chunk_shape or chunk_grid can be provided.")
799-
if chunk_shape is None and chunk_grid is None:
800-
raise ValueError("One of chunk_shape or chunk_grid must be provided.")
783+
"""Create an instance of ArrayV3Metadata."""
801784
filters: tuple[ArrayArrayCodec, ...]
802785
compressors: tuple[BytesBytesCodec, ...]
803786

@@ -824,16 +807,10 @@ def _create_metadata_v3(
824807
else:
825808
fill_value_parsed = fill_value
826809

827-
chunk_grid_meta: ChunkGridMetadata
828-
if chunk_grid is not None:
829-
chunk_grid_meta = chunk_grid
830-
else:
831-
assert chunk_shape is not None # validated above
832-
chunk_grid_meta = RegularChunkGrid(chunk_shape=parse_shapelike(chunk_shape))
833810
return ArrayV3Metadata(
834811
shape=shape,
835812
data_type=dtype,
836-
chunk_grid=chunk_grid_meta,
813+
chunk_grid=chunk_grid,
837814
chunk_key_encoding=chunk_key_encoding_parsed,
838815
fill_value=fill_value_parsed,
839816
codecs=codecs_parsed, # type: ignore[arg-type]
@@ -848,7 +825,7 @@ async def _create_v3(
848825
*,
849826
shape: ShapeLike,
850827
dtype: ZDType[TBaseDType, TBaseScalar],
851-
chunk_shape: tuple[int, ...] | None = None,
828+
chunk_grid: ChunkGridMetadata,
852829
config: ArrayConfig,
853830
fill_value: Any | None = DEFAULT_FILL_VALUE,
854831
chunk_key_encoding: (
@@ -861,12 +838,7 @@ async def _create_v3(
861838
dimension_names: DimensionNamesLike = None,
862839
attributes: dict[str, JSON] | None = None,
863840
overwrite: bool = False,
864-
chunk_grid: ChunkGridMetadata | None = None,
865841
) -> AsyncArrayV3:
866-
if chunk_shape is not None and chunk_grid is not None:
867-
raise ValueError("Only one of chunk_shape or chunk_grid can be provided.")
868-
if chunk_shape is None and chunk_grid is None:
869-
raise ValueError("One of chunk_shape or chunk_grid must be provided.")
870842
if overwrite:
871843
if store_path.store.supports_deletes:
872844
await store_path.delete_dir()
@@ -885,13 +857,12 @@ async def _create_v3(
885857
metadata = cls._create_metadata_v3(
886858
shape=shape,
887859
dtype=dtype,
888-
chunk_shape=chunk_shape,
860+
chunk_grid=chunk_grid,
889861
fill_value=fill_value,
890862
chunk_key_encoding=chunk_key_encoding,
891863
codecs=codecs,
892864
dimension_names=dimension_names,
893865
attributes=attributes,
894-
chunk_grid=chunk_grid,
895866
)
896867

897868
array = cls(metadata=metadata, store_path=store_path, config=config)
@@ -4930,16 +4901,20 @@ async def init_array(
49304901
if order is not None:
49314902
_warn_order_kwarg()
49324903

4904+
grid: ChunkGridMetadata
4905+
if rectilinear_meta is not None:
4906+
grid = rectilinear_meta
4907+
else:
4908+
grid = RegularChunkGrid(chunk_shape=chunks_out)
49334909
meta = AsyncArray._create_metadata_v3(
49344910
shape=shape_parsed,
49354911
dtype=zdtype,
49364912
fill_value=fill_value,
4937-
chunk_shape=chunks_out if rectilinear_meta is None else None,
4913+
chunk_grid=grid,
49384914
chunk_key_encoding=chunk_key_encoding_parsed,
49394915
codecs=codecs_out,
49404916
dimension_names=dimension_names,
49414917
attributes=attributes,
4942-
chunk_grid=rectilinear_meta,
49434918
)
49444919

49454920
arr = AsyncArray(metadata=meta, store_path=store_path, config=config)

src/zarr/core/chunk_grids.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,6 @@ def _decode_dim_spec(dim_spec: JSON, array_extent: int | None = None) -> list[in
335335
raise ValueError(f"Invalid chunk_shapes entry: {dim_spec}")
336336

337337

338-
# Type alias for what users can pass as chunks to create_array
339-
ChunksLike = tuple[int, ...] | list[list[int] | int] | int
340-
341-
342338
def _is_rectilinear_chunks(chunks: Any) -> TypeGuard[Sequence[Sequence[int]]]:
343339
"""Check if chunks is a nested sequence (e.g. [[10, 20], [5, 5]]).
344340

src/zarr/core/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
BytesLike = bytes | bytearray | memoryview
3939
ShapeLike = Iterable[int | np.integer[Any]] | int | np.integer[Any]
40+
ChunksLike = ShapeLike | Sequence[Sequence[int]] | None
4041
# For backwards compatibility
4142
ChunkCoords = tuple[int, ...]
4243
ZarrFormat = Literal[2, 3]

src/zarr/core/metadata/v3.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from zarr.core.common import (
1818
JSON,
1919
ZARR_JSON,
20+
ChunksLike,
2021
DimensionNamesLike,
2122
NamedConfig,
2223
NamedRequiredConfig,
@@ -348,10 +349,38 @@ def from_dict(cls, data: RectilinearChunkGridJSON) -> Self: # type: ignore[over
348349
ChunkGridMetadata = RegularChunkGrid | RectilinearChunkGrid
349350

350351

352+
def resolve_chunks(
353+
chunks: ChunksLike,
354+
shape: tuple[int, ...],
355+
typesize: int,
356+
) -> ChunkGridMetadata:
357+
"""Construct a chunk grid from user-facing input (e.g. ``create_array(chunks=...)``).
358+
359+
Nested sequences like ``[[10, 20], [5, 5]]`` produce a ``RectilinearChunkGrid``.
360+
Flat inputs like ``(10, 10)`` or a scalar ``int`` produce a ``RegularChunkGrid``
361+
after normalization via :func:`~zarr.core.chunk_grids.normalize_chunks`.
362+
363+
See Also
364+
--------
365+
parse_chunk_grid : Deserialize a chunk grid from stored JSON metadata.
366+
"""
367+
from zarr.core.chunk_grids import _is_rectilinear_chunks, normalize_chunks
368+
369+
if _is_rectilinear_chunks(chunks):
370+
return RectilinearChunkGrid(chunk_shapes=tuple(tuple(c) for c in chunks))
371+
372+
return RegularChunkGrid(chunk_shape=normalize_chunks(chunks, shape, typesize))
373+
374+
351375
def parse_chunk_grid(
352376
data: dict[str, JSON] | ChunkGridMetadata | NamedConfig[str, Any],
353377
) -> ChunkGridMetadata:
354-
"""Parse a chunk grid from a metadata dict or pass through an existing instance."""
378+
"""Deserialize a chunk grid from stored JSON metadata or pass through an existing instance.
379+
380+
See Also
381+
--------
382+
resolve_chunks : Construct a chunk grid from user-facing input.
383+
"""
355384
if isinstance(data, (RegularChunkGrid, RectilinearChunkGrid)):
356385
return data
357386

0 commit comments

Comments
 (0)