Skip to content

Commit d05a43c

Browse files
committed
refactor to separate sync and async routines
1 parent 1bb6578 commit d05a43c

File tree

6 files changed

+170
-177
lines changed

6 files changed

+170
-177
lines changed

src/zarr/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
create,
99
create_array,
1010
create_group,
11+
create_hierarchy,
12+
create_nodes,
13+
create_rooted_hierarchy,
1114
empty,
1215
empty_like,
1316
full,
@@ -30,7 +33,7 @@
3033
)
3134
from zarr.core.array import Array, AsyncArray
3235
from zarr.core.config import config
33-
from zarr.core.group import AsyncGroup, Group, create_hierarchy, create_nodes
36+
from zarr.core.group import AsyncGroup, Group
3437

3538
# in case setuptools scm screw up and find version to be 0.0.0
3639
assert not __version__.startswith("0.0.0")
@@ -52,6 +55,7 @@
5255
"create_group",
5356
"create_hierarchy",
5457
"create_nodes",
58+
"create_rooted_hierarchy",
5559
"empty",
5660
"empty_like",
5761
"full",

src/zarr/api/asynchronous.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,14 @@
2323
_warn_write_empty_chunks_kwarg,
2424
parse_dtype,
2525
)
26-
from zarr.core.group import AsyncGroup, ConsolidatedMetadata, GroupMetadata
26+
from zarr.core.group import (
27+
AsyncGroup,
28+
ConsolidatedMetadata,
29+
GroupMetadata,
30+
create_hierarchy,
31+
create_nodes,
32+
create_rooted_hierarchy,
33+
)
2734
from zarr.core.metadata import ArrayMetadataDict, ArrayV2Metadata, ArrayV3Metadata
2835
from zarr.core.metadata.v2 import _default_compressor, _default_filters
2936
from zarr.errors import NodeTypeValidationError
@@ -48,6 +55,9 @@
4855
"copy_store",
4956
"create",
5057
"create_array",
58+
"create_hierarchy",
59+
"create_nodes",
60+
"create_rooted_hierarchy",
5161
"empty",
5262
"empty_like",
5363
"full",

src/zarr/api/synchronous.py

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@
88
import zarr.core.array
99
from zarr._compat import _deprecate_positional_args
1010
from zarr.core.array import Array, AsyncArray
11-
from zarr.core.group import Group
12-
from zarr.core.sync import sync
11+
from zarr.core.group import Group, GroupMetadata, _parse_async_node
12+
from zarr.core.sync import _collect_aiterator, sync
1313

1414
if TYPE_CHECKING:
15-
from collections.abc import Iterable
15+
from collections.abc import Iterable, Iterator
1616

1717
import numpy as np
1818
import numpy.typing as npt
1919

2020
from zarr.abc.codec import Codec
21+
from zarr.abc.store import Store
2122
from zarr.api.asynchronous import ArrayLike, PathLike
2223
from zarr.core.array import (
2324
CompressorsLike,
@@ -36,6 +37,7 @@
3637
ShapeLike,
3738
ZarrFormat,
3839
)
40+
from zarr.core.metadata import ArrayV2Metadata, ArrayV3Metadata
3941
from zarr.storage import StoreLike
4042

4143
__all__ = [
@@ -46,6 +48,9 @@
4648
"copy_store",
4749
"create",
4850
"create_array",
51+
"create_hierarchy",
52+
"create_nodes",
53+
"create_rooted_hierarchy",
4954
"empty",
5055
"empty_like",
5156
"full",
@@ -1124,3 +1129,98 @@ def zeros_like(a: ArrayLike, **kwargs: Any) -> Array:
11241129
The new array.
11251130
"""
11261131
return Array(sync(async_api.zeros_like(a, **kwargs)))
1132+
1133+
1134+
def create_hierarchy(
1135+
store: Store,
1136+
path: str,
1137+
nodes: dict[str, GroupMetadata | ArrayV2Metadata | ArrayV3Metadata],
1138+
overwrite: bool = False,
1139+
allow_root: bool = True,
1140+
) -> Iterator[Group | Array]:
1141+
"""
1142+
Create a complete zarr hierarchy concurrently. Groups that are implicitly defined by the input
1143+
will be created as needed.
1144+
1145+
This function takes a parsed hierarchy dictionary and creates all the nodes in the hierarchy
1146+
concurrently. Arrays and Groups are yielded in the order they are created.
1147+
1148+
Parameters
1149+
----------
1150+
store : Store
1151+
The storage backend to use.
1152+
path : str
1153+
The name of the root of the created hierarchy. Every key in ``nodes`` will be prefixed with
1154+
``path`` prior to creating nodes.
1155+
nodes : dict[str, GroupMetadata | ArrayV3Metadata | ArrayV2Metadata]
1156+
A dictionary defining the hierarchy. The keys are the paths of the nodes
1157+
in the hierarchy, and the values are the metadata of the nodes. The
1158+
metadata must be either an instance of GroupMetadata, ArrayV3Metadata
1159+
or ArrayV2Metadata.
1160+
allow_root : bool
1161+
Whether to allow a root node to be created. If ``False``, attempting to create a root node
1162+
will result in an error. Use this option when calling this function as part of a method
1163+
defined on ``AsyncGroup`` instances, because in this case the root node has already been
1164+
created.
1165+
1166+
Yields
1167+
------
1168+
Group | Array
1169+
The created nodes in the order they are created.
1170+
"""
1171+
coro = async_api.create_hierarchy(
1172+
store=store, path=path, nodes=nodes, overwrite=overwrite, allow_root=allow_root
1173+
)
1174+
1175+
for result in sync(_collect_aiterator(coro)):
1176+
yield _parse_async_node(result)
1177+
1178+
1179+
def create_nodes(
1180+
*, store: Store, path: str, nodes: dict[str, GroupMetadata | ArrayV2Metadata | ArrayV3Metadata]
1181+
) -> Iterator[Group | Array]:
1182+
"""Create a collection of arrays and / or groups concurrently.
1183+
1184+
Note: no attempt is made to validate that these arrays and / or groups collectively form a
1185+
valid Zarr hierarchy. It is the responsibility of the caller of this function to ensure that
1186+
the ``nodes`` parameter satisfies any correctness constraints.
1187+
1188+
Parameters
1189+
----------
1190+
store : Store
1191+
The storage backend to use.
1192+
path : str
1193+
The name of the root of the created hierarchy. Every key in ``nodes`` will be prefixed with
1194+
``path`` prior to creating nodes.
1195+
nodes : dict[str, GroupMetadata | ArrayV3Metadata | ArrayV2Metadata]
1196+
A dictionary defining the hierarchy. The keys are the paths of the nodes
1197+
in the hierarchy, and the values are the metadata of the nodes. The
1198+
metadata must be either an instance of GroupMetadata, ArrayV3Metadata
1199+
or ArrayV2Metadata.
1200+
1201+
Yields
1202+
------
1203+
Group | Array
1204+
The created nodes in the order they are created.
1205+
"""
1206+
coro = async_api.create_nodes(store=store, path=path, nodes=nodes)
1207+
1208+
for result in sync(_collect_aiterator(coro)):
1209+
yield _parse_async_node(result)
1210+
1211+
1212+
def create_rooted_hierarchy(
1213+
*,
1214+
store: Store,
1215+
path: str,
1216+
nodes: dict[str, GroupMetadata | ArrayV2Metadata | ArrayV3Metadata],
1217+
overwrite: bool = False,
1218+
) -> Group | Array:
1219+
"""
1220+
Create a ``Group`` from a store and a dict of metadata documents. Calls the async method
1221+
``_create_rooted_hierarchy`` and waits for the result.
1222+
"""
1223+
async_node = sync(
1224+
async_api.create_rooted_hierarchy(store=store, path=path, nodes=nodes, overwrite=overwrite)
1225+
)
1226+
return _parse_async_node(async_node)

src/zarr/core/group.py

Lines changed: 10 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
from zarr.core.config import config
5858
from zarr.core.metadata import ArrayV2Metadata, ArrayV3Metadata
5959
from zarr.core.metadata.v3 import V3JsonEncoder
60-
from zarr.core.sync import SyncMixin, _collect_aiterator, sync
60+
from zarr.core.sync import SyncMixin, sync
6161
from zarr.errors import (
6262
ContainsArrayError,
6363
ContainsGroupError,
@@ -1435,7 +1435,7 @@ async def create_hierarchy(
14351435
semaphore = asyncio.Semaphore(config.get("async.concurrency"))
14361436

14371437
try:
1438-
async for node in create_hierarchy_a(
1438+
async for node in create_hierarchy(
14391439
store=self.store,
14401440
path=self.path,
14411441
nodes=nodes,
@@ -2081,7 +2081,7 @@ def create_hierarchy(
20812081
nodes: dict[str, ArrayV2Metadata | ArrayV3Metadata | GroupMetadata],
20822082
*,
20832083
overwrite: bool = False,
2084-
) -> Iterator[AsyncGroup | AsyncArray[ArrayV2Metadata] | AsyncArray[ArrayV3Metadata]]:
2084+
) -> Iterator[Group | Array]:
20852085
"""
20862086
Create a hierarchy of arrays or groups rooted at this group.
20872087
@@ -2116,7 +2116,8 @@ def create_hierarchy(
21162116
<AsyncGroup memory://123209880766144/a/b/c>
21172117
<AsyncGroup memory://123209880766144/a/b>
21182118
"""
2119-
yield from self._sync_iter(self._async_group.create_hierarchy(nodes, overwrite=overwrite))
2119+
for node in self._sync_iter(self._async_group.create_hierarchy(nodes, overwrite=overwrite)):
2120+
yield _parse_async_node(node)
21202121

21212122
def keys(self) -> Generator[str, None]:
21222123
"""Return an iterator over group member names.
@@ -2854,7 +2855,7 @@ def array(
28542855
)
28552856

28562857

2857-
async def create_hierarchy_a(
2858+
async def create_hierarchy(
28582859
*,
28592860
store: Store,
28602861
path: str,
@@ -2967,58 +2968,11 @@ async def create_hierarchy_a(
29672968
k: v for k, v in nodes_parsed.items() if k not in redundant_implicit_groups
29682969
}
29692970

2970-
async for node in create_nodes_a(
2971-
store=store, path=path, nodes=nodes_parsed, semaphore=semaphore
2972-
):
2971+
async for node in create_nodes(store=store, path=path, nodes=nodes_parsed, semaphore=semaphore):
29732972
yield node
29742973

29752974

2976-
def create_hierarchy(
2977-
store: Store,
2978-
path: str,
2979-
nodes: dict[str, GroupMetadata | ArrayV2Metadata | ArrayV3Metadata],
2980-
overwrite: bool = False,
2981-
allow_root: bool = True,
2982-
) -> Iterator[Group | Array]:
2983-
"""
2984-
Create a complete zarr hierarchy concurrently. Groups that are implicitly defined by the input
2985-
will be created as needed.
2986-
2987-
This function takes a parsed hierarchy dictionary and creates all the nodes in the hierarchy
2988-
concurrently. Arrays and Groups are yielded in the order they are created.
2989-
2990-
Parameters
2991-
----------
2992-
store : Store
2993-
The storage backend to use.
2994-
path : str
2995-
The name of the root of the created hierarchy. Every key in ``nodes`` will be prefixed with
2996-
``path`` prior to creating nodes.
2997-
nodes : dict[str, GroupMetadata | ArrayV3Metadata | ArrayV2Metadata]
2998-
A dictionary defining the hierarchy. The keys are the paths of the nodes
2999-
in the hierarchy, and the values are the metadata of the nodes. The
3000-
metadata must be either an instance of GroupMetadata, ArrayV3Metadata
3001-
or ArrayV2Metadata.
3002-
allow_root : bool
3003-
Whether to allow a root node to be created. If ``False``, attempting to create a root node
3004-
will result in an error. Use this option when calling this function as part of a method
3005-
defined on ``AsyncGroup`` instances, because in this case the root node has already been
3006-
created.
3007-
3008-
Yields
3009-
------
3010-
Group | Array
3011-
The created nodes in the order they are created.
3012-
"""
3013-
coro = create_hierarchy_a(
3014-
store=store, path=path, nodes=nodes, overwrite=overwrite, allow_root=allow_root
3015-
)
3016-
3017-
for result in sync(_collect_aiterator(coro)):
3018-
yield _parse_async_node(result)
3019-
3020-
3021-
async def create_nodes_a(
2975+
async def create_nodes(
30222976
*,
30232977
store: Store,
30242978
path: str,
@@ -3119,46 +3073,6 @@ async def create_nodes_a(
31193073
continue
31203074

31213075

3122-
def create_nodes(
3123-
*,
3124-
store: Store,
3125-
path: str,
3126-
nodes: dict[str, GroupMetadata | ArrayV2Metadata | ArrayV3Metadata],
3127-
semaphore: asyncio.Semaphore | None = None,
3128-
) -> Iterator[Group | Array]:
3129-
"""Create a collection of arrays and / or groups concurrently.
3130-
3131-
Note: no attempt is made to validate that these arrays and / or groups collectively form a
3132-
valid Zarr hierarchy. It is the responsibility of the caller of this function to ensure that
3133-
the ``nodes`` parameter satisfies any correctness constraints.
3134-
3135-
Parameters
3136-
----------
3137-
store : Store
3138-
The storage backend to use.
3139-
path : str
3140-
The name of the root of the created hierarchy. Every key in ``nodes`` will be prefixed with
3141-
``path`` prior to creating nodes.
3142-
nodes : dict[str, GroupMetadata | ArrayV3Metadata | ArrayV2Metadata]
3143-
A dictionary defining the hierarchy. The keys are the paths of the nodes
3144-
in the hierarchy, and the values are the metadata of the nodes. The
3145-
metadata must be either an instance of GroupMetadata, ArrayV3Metadata
3146-
or ArrayV2Metadata.
3147-
semaphore : asyncio.Semaphore | None
3148-
An optional semaphore to limit the number of concurrent tasks. If not
3149-
provided, the number of concurrent tasks is unlimited.
3150-
3151-
Yields
3152-
------
3153-
Group | Array
3154-
The created nodes in the order they are created.
3155-
"""
3156-
coro = create_nodes_a(store=store, path=path, nodes=nodes, semaphore=semaphore)
3157-
3158-
for result in sync(_collect_aiterator(coro)):
3159-
yield _parse_async_node(result)
3160-
3161-
31623076
T = TypeVar("T")
31633077

31643078

@@ -3611,7 +3525,7 @@ def _persist_metadata(
36113525
)
36123526

36133527

3614-
async def _create_rooted_hierarchy_a(
3528+
async def create_rooted_hierarchy(
36153529
*,
36163530
store: Store,
36173531
path: str,
@@ -3639,25 +3553,8 @@ async def _create_rooted_hierarchy_a(
36393553

36403554
nodes_created = {
36413555
x.path: x
3642-
async for x in create_hierarchy_a(
3556+
async for x in create_hierarchy(
36433557
store=store, path=path, nodes=nodes, semaphore=semaphore, overwrite=overwrite
36443558
)
36453559
}
36463560
return nodes_created[_join_paths([path, root_key])]
3647-
3648-
3649-
def _create_rooted_hierarchy(
3650-
*,
3651-
store: Store,
3652-
path: str,
3653-
nodes: dict[str, GroupMetadata | ArrayV2Metadata | ArrayV3Metadata],
3654-
overwrite: bool = False,
3655-
) -> Group | Array:
3656-
"""
3657-
Create a ``Group`` from a store and a dict of metadata documents. Calls the async method
3658-
``_create_rooted_hierarchy`` and waits for the result.
3659-
"""
3660-
async_node = sync(
3661-
_create_rooted_hierarchy_a(store=store, path=path, nodes=nodes, overwrite=overwrite)
3662-
)
3663-
return _parse_async_node(async_node)

tests/test_api.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import zarr
1010
import zarr.api.asynchronous
11+
import zarr.api.synchronous
1112
import zarr.core.group
1213
from zarr import Array, Group
1314
from zarr.abc.store import Store
@@ -1121,3 +1122,10 @@ def test_open_array_with_mode_r_plus(store: Store) -> None:
11211122
assert isinstance(z2, Array)
11221123
assert (z2[:] == 1).all()
11231124
z2[:] = 3
1125+
1126+
1127+
def test_api_exports() -> None:
1128+
"""
1129+
Test that the sync API and the async API export the same objects
1130+
"""
1131+
assert zarr.api.asynchronous.__all__ == zarr.api.synchronous.__all__

0 commit comments

Comments
 (0)