diff --git a/changes/xxxx.doc.rst b/changes/xxxx.doc.rst new file mode 100644 index 0000000000..115b423892 --- /dev/null +++ b/changes/xxxx.doc.rst @@ -0,0 +1 @@ +Added migration guide for ``zarr.storage.contains_array()`` and ``zarr.storage.contains_group()``. diff --git a/changes/xxxx.feature.rst b/changes/xxxx.feature.rst new file mode 100644 index 0000000000..97d1590a73 --- /dev/null +++ b/changes/xxxx.feature.rst @@ -0,0 +1 @@ +Added `zarr.storage.get_node` to get a node within a store. diff --git a/docs/user-guide/v3_migration.rst b/docs/user-guide/v3_migration.rst index a6258534e4..0bd59fdeb1 100644 --- a/docs/user-guide/v3_migration.rst +++ b/docs/user-guide/v3_migration.rst @@ -123,8 +123,7 @@ The Group class The Store class ~~~~~~~~~~~~~~~ -The Store API has changed significant in Zarr-Python 3. The most notable changes to the -Store API are: +The Store API has changed significant in Zarr-Python 3. Store Import Paths ^^^^^^^^^^^^^^^^^^ @@ -156,8 +155,30 @@ Common replacements: change ensures that all store methods are non-blocking and are as performant as possible. -Beyond the changes store interface, a number of deprecated stores were also removed in -Zarr-Python 3. See :issue:`1274` for more details on the removal of these stores. +Removed store API +^^^^^^^^^^^^^^^^^ + +``zarr.storage.contains_array()`` and ``zarr.storage.contains_group()`` have been removed. +Instead, use `zarr.storage.get_node`, and check the return type, e.g., + +>>> import zarr +>>> import zarr.storage +>>> +>>> store = zarr.storage.MemoryStore() +>>> root = zarr.create_group(store=store) +>>> isinstance(zarr.storage.get_node(store, path="", zarr_format=3), zarr.Group) +True +>>> +>>> root.create_array("b", shape=(1,), dtype=int) + +>>> isinstance(zarr.storage.get_node(store, path="b", zarr_format=3), zarr.Array) +True + +Removed stores +^^^^^^^^^^^^^^ + +A number of deprecated stores were removed in Zarr-Python 3. See :issue:`1274` for more +details on the removal of these stores. - ``N5Store`` - see https://github.com/zarr-developers/n5py for an alternative interface to N5 formatted data. diff --git a/src/zarr/core/sync_group.py b/src/zarr/core/sync_group.py index 39d8a17992..57527535a6 100644 --- a/src/zarr/core/sync_group.py +++ b/src/zarr/core/sync_group.py @@ -6,7 +6,6 @@ from zarr.core.group import create_hierarchy as create_hierarchy_async from zarr.core.group import create_nodes as create_nodes_async from zarr.core.group import create_rooted_hierarchy as create_rooted_hierarchy_async -from zarr.core.group import get_node as get_node_async from zarr.core.sync import _collect_aiterator, sync if TYPE_CHECKING: @@ -14,7 +13,6 @@ from zarr.abc.store import Store from zarr.core.array import Array - from zarr.core.common import ZarrFormat from zarr.core.metadata import ArrayV2Metadata, ArrayV3Metadata @@ -138,24 +136,3 @@ def create_rooted_hierarchy( """ async_node = sync(create_rooted_hierarchy_async(store=store, nodes=nodes, overwrite=overwrite)) return _parse_async_node(async_node) - - -def get_node(store: Store, path: str, zarr_format: ZarrFormat) -> Array | Group: - """ - Get an Array or Group from a path in a Store. - - Parameters - ---------- - store : Store - The store-like object to read from. - path : str - The path to the node to read. - zarr_format : {2, 3} - The zarr format of the node to read. - - Returns - ------- - Array | Group - """ - - return _parse_async_node(sync(get_node_async(store=store, path=path, zarr_format=zarr_format))) diff --git a/src/zarr/storage/__init__.py b/src/zarr/storage/__init__.py index 6721139375..60c51d347e 100644 --- a/src/zarr/storage/__init__.py +++ b/src/zarr/storage/__init__.py @@ -3,7 +3,7 @@ from types import ModuleType from typing import Any -from zarr.storage._common import StoreLike, StorePath +from zarr.storage._common import StoreLike, StorePath, get_node from zarr.storage._fsspec import FsspecStore from zarr.storage._local import LocalStore from zarr.storage._logging import LoggingStore @@ -23,6 +23,7 @@ "StorePath", "WrapperStore", "ZipStore", + "get_node", ] diff --git a/src/zarr/storage/_common.py b/src/zarr/storage/_common.py index d81369f142..e91ca1fe76 100644 --- a/src/zarr/storage/_common.py +++ b/src/zarr/storage/_common.py @@ -7,12 +7,14 @@ from zarr.abc.store import ByteRequest, Store from zarr.core.buffer import Buffer, default_buffer_prototype from zarr.core.common import ZARR_JSON, ZARRAY_JSON, ZGROUP_JSON, AccessModeLiteral, ZarrFormat +from zarr.core.sync import sync from zarr.errors import ContainsArrayAndGroupError, ContainsArrayError, ContainsGroupError from zarr.storage._local import LocalStore from zarr.storage._memory import MemoryStore from zarr.storage._utils import normalize_path if TYPE_CHECKING: + from zarr import Array, Group from zarr.core.buffer import BufferPrototype @@ -505,3 +507,26 @@ async def contains_group(store_path: StorePath, zarr_format: ZarrFormat) -> bool return await (store_path / ZGROUP_JSON).exists() msg = f"Invalid zarr_format provided. Got {zarr_format}, expected 2 or 3" # type: ignore[unreachable] raise ValueError(msg) + + +def get_node(store: Store, path: str, zarr_format: ZarrFormat) -> Array | Group: + """ + Get an Array or Group from a path in a Store. + + Parameters + ---------- + store : Store + The store-like object to read from. + path : str + The path to the node to read. + zarr_format : {2, 3} + The zarr format of the node to read. + + Returns + ------- + Array | Group + """ + from zarr.core.group import _parse_async_node + from zarr.core.group import get_node as get_node_async + + return _parse_async_node(sync(get_node_async(store=store, path=path, zarr_format=zarr_format))) diff --git a/tests/test_group.py b/tests/test_group.py index 1e4f31b5d6..5726fb0faf 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -1534,7 +1534,7 @@ def test_create_nodes_concurrency_limit(store: MemoryStore) -> None: (zarr.core.group.create_hierarchy, zarr.core.sync_group.create_hierarchy), (zarr.core.group.create_nodes, zarr.core.sync_group.create_nodes), (zarr.core.group.create_rooted_hierarchy, zarr.core.sync_group.create_rooted_hierarchy), - (zarr.core.group.get_node, zarr.core.sync_group.get_node), + (zarr.core.group.get_node, zarr.storage.get_node), ], ) def test_consistent_signatures( @@ -1594,7 +1594,9 @@ async def test_create_hierarchy( else: raise ValueError(f"Invalid impl: {impl}") if not overwrite: - extra_group = sync_group.get_node(store=store, path="group/extra", zarr_format=zarr_format) + extra_group = zarr.storage.get_node( + store=store, path="group/extra", zarr_format=zarr_format + ) assert extra_group.metadata.attributes == {"path": "group/extra"} else: with pytest.raises(FileNotFoundError): @@ -1713,7 +1715,7 @@ async def test_group_create_hierarchy( assert all_members[k].metadata == v == extant_created[k].metadata # ensure that we left the root group as-is assert ( - sync_group.get_node(store=store, path=group_path, zarr_format=zarr_format).attrs.asdict() + zarr.storage.get_node(store=store, path=group_path, zarr_format=zarr_format).attrs.asdict() == root_attrs )