Skip to content

Commit 5d8445b

Browse files
committed
add create_array, create_group, and tests
1 parent d95eba8 commit 5d8445b

File tree

5 files changed

+288
-9
lines changed

5 files changed

+288
-9
lines changed

src/zarr/api/asynchronous.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,54 @@ async def group(
644644
)
645645

646646

647+
async def create_group(
648+
*,
649+
store: StoreLike,
650+
path: str | None = None,
651+
overwrite: bool = False,
652+
zarr_format: ZarrFormat | None = None,
653+
attributes: dict[str, Any] | None = None,
654+
storage_options: dict[str, Any] | None = None,
655+
) -> AsyncGroup:
656+
"""Create a group.
657+
658+
Parameters
659+
----------
660+
store : Store or str
661+
Store or path to directory in file system.
662+
path : str, optional
663+
Group path within store.
664+
overwrite : bool, optional
665+
If True, pre-existing data at ``path`` will be deleted before
666+
creating the group.
667+
zarr_format : {2, 3, None}, optional
668+
The zarr format to use when saving.
669+
storage_options : dict
670+
If using an fsspec URL to create the store, these will be passed to
671+
the backend implementation. Ignored otherwise.
672+
673+
Returns
674+
-------
675+
g : group
676+
The new group.
677+
"""
678+
679+
if zarr_format is None:
680+
zarr_format = _default_zarr_version()
681+
682+
# TODO: fix this when modes make sense. It should be `w` for overwriting, `w-` otherwise
683+
mode: Literal["a"] = "a"
684+
685+
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
686+
687+
return await AsyncGroup.from_store(
688+
store=store_path,
689+
zarr_format=zarr_format,
690+
exists_ok=overwrite,
691+
attributes=attributes,
692+
)
693+
694+
647695
async def open_group(
648696
store: StoreLike | None = None,
649697
*, # Note: this is a change from v2
@@ -752,6 +800,7 @@ async def open_group(
752800

753801
async def read_group(
754802
store: StoreLike,
803+
*,
755804
path: str | None = None,
756805
zarr_format: ZarrFormat | None = None,
757806
storage_options: dict[str, Any] | None = None,
@@ -810,6 +859,127 @@ async def read_group(
810859
)
811860

812861

862+
async def create_array(
863+
store: str | StoreLike,
864+
*,
865+
shape: ChunkCoords,
866+
chunks: ChunkCoords | None = None, # TODO: v2 allowed chunks=True
867+
dtype: npt.DTypeLike | None = None,
868+
compressor: dict[str, JSON] | None = None, # TODO: default and type change
869+
fill_value: Any | None = 0, # TODO: need type
870+
order: MemoryOrder | None = None,
871+
overwrite: bool = False,
872+
path: PathLike | None = None,
873+
filters: list[dict[str, JSON]] | None = None, # TODO: type has changed
874+
dimension_separator: Literal[".", "/"] | None = None,
875+
zarr_format: ZarrFormat | None = None,
876+
attributes: dict[str, JSON] | None = None,
877+
# v3 only
878+
chunk_shape: ChunkCoords | None = None,
879+
chunk_key_encoding: (
880+
ChunkKeyEncoding
881+
| tuple[Literal["default"], Literal[".", "/"]]
882+
| tuple[Literal["v2"], Literal[".", "/"]]
883+
| None
884+
) = None,
885+
codecs: Iterable[Codec | dict[str, JSON]] | None = None,
886+
dimension_names: Iterable[str] | None = None,
887+
storage_options: dict[str, Any] | None = None,
888+
**kwargs: Any,
889+
) -> AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata]:
890+
"""Create an array.
891+
892+
Parameters
893+
----------
894+
shape : int or tuple of ints
895+
Array shape.
896+
chunks : int or tuple of ints, optional
897+
Chunk shape. If True, will be guessed from `shape` and `dtype`. If
898+
False, will be set to `shape`, i.e., single chunk for the whole array.
899+
If an int, the chunk size in each dimension will be given by the value
900+
of `chunks`. Default is True.
901+
dtype : str or dtype, optional
902+
NumPy dtype.
903+
compressor : Codec, optional
904+
Primary compressor.
905+
fill_value : object
906+
Default value to use for uninitialized portions of the array.
907+
order : {'C', 'F'}, optional
908+
Memory layout to be used within each chunk.
909+
Default is set in Zarr's config (`array.order`).
910+
store : Store or str
911+
Store or path to directory in file system or name of zip file.
912+
overwrite : bool, optional
913+
If True, delete all pre-existing data in `store` at `path` before
914+
creating the array.
915+
path : str, optional
916+
Path under which array is stored.
917+
filters : sequence of Codecs, optional
918+
Sequence of filters to use to encode chunk data prior to compression.
919+
dimension_separator : {'.', '/'}, optional
920+
Separator placed between the dimensions of a chunk.
921+
zarr_format : {2, 3, None}, optional
922+
The zarr format to use when saving.
923+
storage_options : dict
924+
If using an fsspec URL to create the store, these will be passed to
925+
the backend implementation. Ignored otherwise.
926+
927+
Returns
928+
-------
929+
z : array
930+
The array.
931+
"""
932+
933+
if zarr_format is None:
934+
zarr_format = _default_zarr_version()
935+
936+
if zarr_format == 2 and chunks is None:
937+
chunks = shape
938+
elif zarr_format == 3 and chunk_shape is None:
939+
if chunks is not None:
940+
chunk_shape = chunks
941+
chunks = None
942+
else:
943+
chunk_shape = shape
944+
945+
if dimension_separator is not None:
946+
if zarr_format == 3:
947+
raise ValueError(
948+
"dimension_separator is not supported for zarr format 3, use chunk_key_encoding instead"
949+
)
950+
else:
951+
warnings.warn(
952+
"dimension_separator is not yet implemented",
953+
RuntimeWarning,
954+
stacklevel=2,
955+
)
956+
957+
# TODO: fix this when modes make sense. It should be `w` for overwriting, `w-` otherwise
958+
mode: Literal["a"] = "a"
959+
960+
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
961+
962+
return await AsyncArray.create(
963+
store_path,
964+
shape=shape,
965+
chunks=chunks,
966+
dtype=dtype,
967+
compressor=compressor,
968+
fill_value=fill_value,
969+
exists_ok=overwrite,
970+
filters=filters,
971+
dimension_separator=dimension_separator,
972+
zarr_format=zarr_format,
973+
chunk_shape=chunk_shape,
974+
chunk_key_encoding=chunk_key_encoding,
975+
codecs=codecs,
976+
dimension_names=dimension_names,
977+
attributes=attributes,
978+
order=order,
979+
**kwargs,
980+
)
981+
982+
813983
async def create(
814984
shape: ChunkCoords,
815985
*, # Note: this is a change from v2
@@ -996,6 +1166,7 @@ async def create(
9961166

9971167
async def read_array(
9981168
store: StoreLike,
1169+
*,
9991170
path: str | None = None,
10001171
zarr_format: ZarrFormat | None = None,
10011172
storage_options: dict[str, Any] | None = None,

src/zarr/api/synchronous.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,34 @@ def open_group(
242242
)
243243

244244

245+
def create_group(
246+
store: StoreLike,
247+
*,
248+
path: str | None = None,
249+
zarr_format: ZarrFormat | None = None,
250+
overwrite: bool = False,
251+
attributes: dict[str, Any] | None = None,
252+
storage_options: dict[str, Any] | None = None,
253+
) -> Group:
254+
return Group(
255+
sync(
256+
async_api.create_group(
257+
store=store,
258+
path=path,
259+
overwrite=overwrite,
260+
storage_options=storage_options,
261+
zarr_format=zarr_format,
262+
attributes=attributes,
263+
)
264+
)
265+
)
266+
267+
245268
def read_group(
246-
store: StoreLike | None = None,
269+
store: StoreLike,
270+
*,
247271
path: str | None = None,
248-
storage_options: dict[str, Any] | None = None, # not used in async api
272+
storage_options: dict[str, Any] | None = None,
249273
zarr_format: ZarrFormat | None = None,
250274
use_consolidated: bool | str | None = None,
251275
) -> Group:
@@ -264,6 +288,10 @@ def create(*args: Any, **kwargs: Any) -> Array:
264288
return Array(sync(async_api.create(*args, **kwargs)))
265289

266290

291+
def create_array(*args: Any, **kwargs: Any) -> Array:
292+
return Array(sync(async_api.create_array(*args, **kwargs)))
293+
294+
267295
def read_array(*args: Any, **kwargs: Any) -> Array:
268296
return Array(sync(async_api.read_array(*args, **kwargs)))
269297

src/zarr/core/array.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import json
4+
import warnings
45
from asyncio import gather
56
from dataclasses import dataclass, field
67
from itertools import starmap
@@ -144,9 +145,9 @@ async def get_array_metadata(
144145
(store_path / ZATTRS_JSON).get(),
145146
)
146147
if zarr_json_bytes is not None and zarray_bytes is not None:
147-
# TODO: revisit this exception type
148-
# alternatively, we could warn and favor v3
149-
raise ValueError("Both zarr.json and .zarray objects exist")
148+
# wwarn and favor v3
149+
msg = f"Both zarr.json (zarr v3) and .zarray (zarr v2) metadata objects exist at {store_path}."
150+
warnings.warn(msg, stacklevel=1)
150151
if zarr_json_bytes is None and zarray_bytes is None:
151152
raise FileNotFoundError(store_path)
152153
# set zarr_format based on which keys were found

src/zarr/core/group.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -486,9 +486,9 @@ async def open(
486486
(store_path / str(consolidated_key)).get(),
487487
)
488488
if zarr_json_bytes is not None and zgroup_bytes is not None:
489-
# TODO: revisit this exception type
490-
# alternatively, we could warn and favor v3
491-
raise ValueError("Both zarr.json and .zgroup objects exist")
489+
# we could warn and favor v3
490+
msg = f"Both zarr.json (zarr v3) and .zgroup (zarr v2) metadata objects exist at {store_path}."
491+
warnings.warn(msg, stacklevel=1)
492492
if zarr_json_bytes is None and zgroup_bytes is None:
493493
raise FileNotFoundError(
494494
f"could not find zarr.json or .zgroup objects in {store_path}"

0 commit comments

Comments
 (0)