Skip to content

Commit d1eb448

Browse files
committed
use new errors
1 parent 5e81b1e commit d1eb448

File tree

4 files changed

+182
-131
lines changed

4 files changed

+182
-131
lines changed

src/zarr/api/asynchronous.py

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
ConsolidatedMetadata,
2929
GroupMetadata,
3030
create_hierarchy,
31+
get_node,
3132
)
3233
from zarr.core.metadata import ArrayMetadataDict, ArrayV2Metadata, ArrayV3Metadata
3334
from zarr.core.metadata.v2 import _default_compressor, _default_filters
34-
from zarr.errors import NodeTypeValidationError
35+
from zarr.errors import ArrayNotFoundError, GroupNotFoundError, NodeNotFoundError, NodeTypeValidationError
3536
from zarr.storage._common import make_store_path
37+
import contextlib
3638

3739
if TYPE_CHECKING:
3840
from collections.abc import Iterable
@@ -315,30 +317,37 @@ async def open(
315317
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
316318

317319
store_path = await make_store_path(store, mode=mode, path=path, storage_options=storage_options)
320+
extant_node: AsyncArray[Any] | AsyncGroup | None = None
321+
if mode in ('r', 'r+', 'a', 'w-'):
322+
# we need to check for an existing node.
323+
if zarr_format is None:
324+
try:
325+
extant_node = await get_node(store_path.store, store_path.path, zarr_format=3)
326+
except NodeNotFoundError:
327+
with contextlib.suppress(NodeNotFoundError):
328+
extant_node = await get_node(store_path.store, store_path.path, zarr_format=2)
329+
else:
330+
with contextlib.suppress(NodeNotFoundError):
331+
extant_node = await get_node(store_path.store, store_path.path, zarr_format=zarr_format)
318332

319-
# TODO: the mode check below seems wrong!
320-
if "shape" not in kwargs and mode in {"a", "r", "r+", "w"}:
321-
try:
322-
metadata_dict = await get_array_metadata(store_path, zarr_format=zarr_format)
323-
# TODO: remove this cast when we fix typing for array metadata dicts
324-
_metadata_dict = cast(ArrayMetadataDict, metadata_dict)
325-
# for v2, the above would already have raised an exception if not an array
326-
zarr_format = _metadata_dict["zarr_format"]
327-
is_v3_array = zarr_format == 3 and _metadata_dict.get("node_type") == "array"
328-
if is_v3_array or zarr_format == 2:
329-
return AsyncArray(store_path=store_path, metadata=_metadata_dict)
330-
except (AssertionError, FileNotFoundError, NodeTypeValidationError):
331-
pass
332-
return await open_group(store=store_path, zarr_format=zarr_format, mode=mode, **kwargs)
333-
334-
try:
335-
return await open_array(store=store_path, zarr_format=zarr_format, mode=mode, **kwargs)
336-
except (KeyError, NodeTypeValidationError):
337-
# KeyError for a missing key
338-
# NodeTypeValidationError for failing to parse node metadata as an array when it's
339-
# actually a group
340-
return await open_group(store=store_path, zarr_format=zarr_format, mode=mode, **kwargs)
333+
if mode in ('r', 'r+') and extant_node is None:
334+
if zarr_format is None:
335+
msg = (
336+
"No Zarr V2 or V3 metadata documents were found in store "
337+
f"{store_path.store!r} at path {store_path.path!r}."
338+
)
339+
else:
340+
msg = (
341+
f"No Zarr V{zarr_format} metadata documents were found in store "
342+
f"{store_path.store!r} at path {store_path.path!r}."
343+
)
344+
raise NodeNotFoundError(msg)
341345

346+
else:
347+
if "shape" in kwargs:
348+
return await open_array(store=store_path, mode=mode, zarr_format=zarr_format, **kwargs)
349+
else:
350+
return await open_group(store=store_path, mode=mode, zarr_format=zarr_format, **kwargs)
342351

343352
async def open_consolidated(
344353
*args: Any, use_consolidated: Literal[True] = True, **kwargs: Any
@@ -660,7 +669,7 @@ async def group(
660669

661670
try:
662671
return await AsyncGroup.open(store=store_path, zarr_format=zarr_format)
663-
except (KeyError, FileNotFoundError):
672+
except (KeyError, GroupNotFoundError):
664673
_zarr_format = zarr_format or _default_zarr_format()
665674
return await AsyncGroup.from_store(
666675
store=store_path,
@@ -818,7 +827,7 @@ async def open_group(
818827
return await AsyncGroup.open(
819828
store_path, zarr_format=zarr_format, use_consolidated=use_consolidated
820829
)
821-
except (KeyError, FileNotFoundError):
830+
except GroupNotFoundError:
822831
pass
823832
if mode in _CREATE_MODES:
824833
overwrite = _infer_overwrite(mode)
@@ -829,7 +838,8 @@ async def open_group(
829838
overwrite=overwrite,
830839
attributes=attributes,
831840
)
832-
raise FileNotFoundError(f"Unable to find group: {store_path}")
841+
msg = f"Group metadata was not found in store {store_path.store!r} at path {store_path.path!r}."
842+
raise GroupNotFoundError(msg)
833843

834844

835845
async def create(
@@ -1259,7 +1269,7 @@ async def open_array(
12591269

12601270
try:
12611271
return await AsyncArray.open(store_path, zarr_format=zarr_format)
1262-
except FileNotFoundError:
1272+
except ArrayNotFoundError:
12631273
if not store_path.read_only and mode in _CREATE_MODES:
12641274
overwrite = _infer_overwrite(mode)
12651275
_zarr_format = zarr_format or _default_zarr_format()

src/zarr/core/array.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
)
109109
from zarr.core.metadata.v3 import DataType, parse_node_type_array
110110
from zarr.core.sync import sync
111-
from zarr.errors import MetadataValidationError
111+
from zarr.errors import ArrayNotFoundError, MetadataValidationError
112112
from zarr.registry import (
113113
_parse_array_array_codec,
114114
_parse_array_bytes_codec,
@@ -170,11 +170,19 @@ async def get_array_metadata(
170170
(store_path / ZATTRS_JSON).get(prototype=cpu_buffer_prototype),
171171
)
172172
if zarray_bytes is None:
173-
raise FileNotFoundError(store_path)
173+
msg = (
174+
"A Zarr V2 array metadata document was not found in store "
175+
f"{store_path.store!r} at path {store_path.path!r}."
176+
)
177+
raise ArrayNotFoundError(msg)
174178
elif zarr_format == 3:
175179
zarr_json_bytes = await (store_path / ZARR_JSON).get(prototype=cpu_buffer_prototype)
176180
if zarr_json_bytes is None:
177-
raise FileNotFoundError(store_path)
181+
msg = (
182+
"A Zarr V3 array metadata document was not found in store "
183+
f"{store_path.store!r} at path {store_path.path!r}."
184+
)
185+
raise ArrayNotFoundError(msg)
178186
elif zarr_format is None:
179187
zarr_json_bytes, zarray_bytes, zattrs_bytes = await gather(
180188
(store_path / ZARR_JSON).get(prototype=cpu_buffer_prototype),
@@ -183,17 +191,27 @@ async def get_array_metadata(
183191
)
184192
if zarr_json_bytes is not None and zarray_bytes is not None:
185193
# warn and favor v3
186-
msg = f"Both zarr.json (Zarr format 3) and .zarray (Zarr format 2) metadata objects exist at {store_path}. Zarr v3 will be used."
194+
msg = (
195+
"Both zarr.json (Zarr format 3) and .zarray (Zarr format 2) metadata objects "
196+
f"exist in store {store_path.store!r} at path {store_path.path!r}. "
197+
"The Zarr V3 metadata will be used."
198+
"To open Zarr V2 arrays, set zarr_format=2."
199+
)
187200
warnings.warn(msg, stacklevel=1)
188201
if zarr_json_bytes is None and zarray_bytes is None:
189-
raise FileNotFoundError(store_path)
202+
msg = (
203+
f"Neither zarr.json (Zarr format 3) nor .zarray (Zarr format 2) metadata objects "
204+
f"exist in store {store_path.store!r} at path {store_path.path!r}."
205+
)
206+
raise ArrayNotFoundError(msg)
190207
# set zarr_format based on which keys were found
191208
if zarr_json_bytes is not None:
192209
zarr_format = 3
193210
else:
194211
zarr_format = 2
195212
else:
196-
raise MetadataValidationError("zarr_format", "2, 3, or None", zarr_format)
213+
msg = f"Invalid value for zarr_format. Expected one of 2, 3 or None. Got {zarr_format}." # type: ignore[unreachable]
214+
raise ValueError(msg)
197215

198216
metadata_dict: dict[str, JSON]
199217
if zarr_format == 2:

src/zarr/core/group.py

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@
5151
from zarr.core.metadata import ArrayV2Metadata, ArrayV3Metadata
5252
from zarr.core.metadata.v3 import V3JsonEncoder, _replace_special_floats
5353
from zarr.core.sync import SyncMixin, sync
54-
from zarr.errors import ContainsArrayError, ContainsGroupError, MetadataValidationError
54+
from zarr.errors import (
55+
ArrayNotFoundError,
56+
ContainsArrayError,
57+
ContainsGroupError,
58+
GroupNotFoundError,
59+
MetadataValidationError,
60+
NodeNotFoundError,
61+
)
5562
from zarr.storage import StoreLike, StorePath
5663
from zarr.storage._common import ensure_no_existing_node, make_store_path
5764
from zarr.storage._utils import _join_paths, _normalize_path_keys, normalize_path
@@ -509,7 +516,11 @@ async def open(
509516
*[path.get() for path in paths]
510517
)
511518
if zgroup_bytes is None:
512-
raise FileNotFoundError(store_path)
519+
msg = (
520+
"Zarr V2 group metadata was not found in "
521+
f"store {store_path.store!r} at path {store_path.path!r}."
522+
)
523+
raise GroupNotFoundError(msg)
513524

514525
if use_consolidated or use_consolidated is None:
515526
maybe_consolidated_metadata_bytes = rest[0]
@@ -520,7 +531,11 @@ async def open(
520531
elif zarr_format == 3:
521532
zarr_json_bytes = await (store_path / ZARR_JSON).get()
522533
if zarr_json_bytes is None:
523-
raise FileNotFoundError(store_path)
534+
msg = (
535+
"Zarr V3 group metadata was not found in "
536+
f"{store_path.store!r} at path {store_path.path!r}."
537+
)
538+
raise GroupNotFoundError(store_path.store, store_path.path)
524539
elif zarr_format is None:
525540
(
526541
zarr_json_bytes,
@@ -535,19 +550,27 @@ async def open(
535550
)
536551
if zarr_json_bytes is not None and zgroup_bytes is not None:
537552
# warn and favor v3
538-
msg = f"Both zarr.json (Zarr format 3) and .zgroup (Zarr format 2) metadata objects exist at {store_path}. Zarr format 3 will be used."
553+
msg = (
554+
"Both zarr.json (Zarr format 3) and .zarray (Zarr format 2) metadata objects "
555+
f"exist in store {store_path.store!r} at path {store_path.path!r}. "
556+
"The Zarr V3 metadata will be used."
557+
"To open Zarr V2 groups, set zarr_format=2."
558+
)
539559
warnings.warn(msg, stacklevel=1)
540560
if zarr_json_bytes is None and zgroup_bytes is None:
541-
raise FileNotFoundError(
542-
f"could not find zarr.json or .zgroup objects in {store_path}"
561+
msg = (
562+
f"Neither Zarr V2 nor Zarr V3 Group metadata was not found in store "
563+
f"{store_path.store!r} at path {store_path.path!r}."
543564
)
565+
raise GroupNotFoundError(msg)
544566
# set zarr_format based on which keys were found
545567
if zarr_json_bytes is not None:
546568
zarr_format = 3
547569
else:
548570
zarr_format = 2
549571
else:
550-
raise MetadataValidationError("zarr_format", "2, 3, or None", zarr_format)
572+
msg = f"Invalid value for zarr_format. Expected one of 2, 3 or None. Got {zarr_format}." # type: ignore[unreachable]
573+
raise MetadataValidationError(msg)
551574

552575
if zarr_format == 2:
553576
# this is checked above, asserting here for mypy
@@ -569,7 +592,11 @@ async def open(
569592
# V3 groups are comprised of a zarr.json object
570593
assert zarr_json_bytes is not None
571594
if not isinstance(use_consolidated, bool | None):
572-
raise TypeError("use_consolidated must be a bool or None for Zarr format 3.")
595+
msg = (
596+
"use_consolidated must be a bool or None for Zarr format 3. "
597+
f"Got {use_consolidated}"
598+
)
599+
raise TypeError(msg)
573600

574601
return cls._from_bytes_v3(
575602
store_path,
@@ -628,7 +655,7 @@ def _from_bytes_v3(
628655
) -> AsyncGroup:
629656
group_metadata = json.loads(zarr_json_bytes.to_bytes())
630657
if use_consolidated and group_metadata.get("consolidated_metadata") is None:
631-
msg = f"Consolidated metadata requested with 'use_consolidated=True' but not found in '{store_path.path}'."
658+
msg = f"Consolidated metadata requested with 'use_consolidated=True' but not found in '{store_path.path!r}'."
632659
raise ValueError(msg)
633660

634661
elif use_consolidated is False:
@@ -691,7 +718,7 @@ async def getitem(
691718
return await get_node(
692719
store=store_path.store, path=store_path.path, zarr_format=self.metadata.zarr_format
693720
)
694-
except FileNotFoundError as e:
721+
except (ArrayNotFoundError, GroupNotFoundError) as e:
695722
raise KeyError(key) from e
696723

697724
def _getitem_consolidated(
@@ -2981,7 +3008,7 @@ async def create_hierarchy(
29813008

29823009
for key, value in zip(implicit_group_keys, maybe_extant_groups, strict=True):
29833010
if isinstance(value, BaseException):
2984-
if isinstance(value, FileNotFoundError):
3011+
if isinstance(value, GroupNotFoundError):
29853012
# this is fine -- there was no group there, so we will create one
29863013
pass
29873014
else:
@@ -3025,8 +3052,9 @@ async def create_hierarchy(
30253052
for key, extant_node in extant_node_query.items():
30263053
proposed_node = nodes_parsed[key]
30273054
if isinstance(extant_node, BaseException):
3028-
if isinstance(extant_node, FileNotFoundError):
3029-
# ignore FileNotFoundError, because they represent nodes we can safely create
3055+
if isinstance(extant_node, (ArrayNotFoundError, GroupNotFoundError)):
3056+
# ignore ArrayNotFoundError / GroupNotFoundError.
3057+
# They represent nodes we can safely create.
30303058
pass
30313059
else:
30323060
# Any other exception is a real error
@@ -3209,7 +3237,8 @@ def _parse_hierarchy_dict(
32093237
else:
32103238
if not isinstance(out[subpath], GroupMetadata | ImplicitGroupMarker):
32113239
msg = (
3212-
f"The node at {subpath} contains other nodes, but it is not a Zarr group. "
3240+
f"The node at path {subpath!r} contains other nodes, "
3241+
"but it is not a Zarr group. "
32133242
"This is invalid. Only Zarr groups can contain other nodes."
32143243
)
32153244
raise ValueError(msg)
@@ -3374,7 +3403,8 @@ async def _read_metadata_v3(store: Store, path: str) -> ArrayV3Metadata | GroupM
33743403
_join_paths([path, ZARR_JSON]), prototype=default_buffer_prototype()
33753404
)
33763405
if zarr_json_bytes is None:
3377-
raise FileNotFoundError(path)
3406+
msg = f"Neither array nor group metadata were found in store {store!r} at path {path!r}."
3407+
raise NodeNotFoundError(msg)
33783408
else:
33793409
zarr_json = json.loads(zarr_json_bytes.to_bytes())
33803410
return _build_metadata_v3(zarr_json)
@@ -3383,8 +3413,8 @@ async def _read_metadata_v3(store: Store, path: str) -> ArrayV3Metadata | GroupM
33833413
async def _read_metadata_v2(store: Store, path: str) -> ArrayV2Metadata | GroupMetadata:
33843414
"""
33853415
Given a store_path, return ArrayV2Metadata or GroupMetadata defined by the metadata
3386-
document stored at store_path.path / (.zgroup | .zarray). If no such document is found,
3387-
raise a FileNotFoundError.
3416+
document stored at store_path.path / (.zgroup | .zarray). If no metadata document is found,
3417+
this routine raises a ``NodeNotFoundError``.
33883418
"""
33893419
# TODO: consider first fetching array metadata, and only fetching group metadata when we don't
33903420
# find an array
@@ -3406,8 +3436,12 @@ async def _read_metadata_v2(store: Store, path: str) -> ArrayV2Metadata | GroupM
34063436
zmeta = json.loads(zarray_bytes.to_bytes())
34073437
else:
34083438
if zgroup_bytes is None:
3409-
# neither .zarray or .zgroup were found results in KeyError
3410-
raise FileNotFoundError(path)
3439+
# neither .zarray or .zgroup were found results in NodeNotFoundError
3440+
msg = (
3441+
"Neither array nor group metadata were found in "
3442+
f"{store!r} at path {path!r}."
3443+
)
3444+
raise NodeNotFoundError(msg)
34113445
else:
34123446
zmeta = json.loads(zgroup_bytes.to_bytes())
34133447

@@ -3416,21 +3450,39 @@ async def _read_metadata_v2(store: Store, path: str) -> ArrayV2Metadata | GroupM
34163450

34173451
async def _read_group_metadata_v2(store: Store, path: str) -> GroupMetadata:
34183452
"""
3419-
Read group metadata or error
3453+
Read Zarr V2 group metadata.
34203454
"""
3421-
meta = await _read_metadata_v2(store=store, path=path)
3455+
try:
3456+
meta = await _read_metadata_v2(store=store, path=path)
3457+
except NodeNotFoundError as e:
3458+
# NodeNotFoundError is raised when neither array nor group metadata were found,
3459+
# but since this function is concerned with group metadata,
3460+
# it returns a more specific exception here.
3461+
msg = f"A group metadata document was not found in store {store!r} at path {path!r}."
3462+
raise GroupNotFoundError(msg) from e
34223463
if not isinstance(meta, GroupMetadata):
3423-
raise FileNotFoundError(f"Group metadata was not found in {store} at {path}")
3464+
msg = (f"Group metadata was not found in store {store!r} at path {path!r}. ",)
3465+
"An array metadata document was found there instead."
3466+
raise GroupNotFoundError(msg)
34243467
return meta
34253468

34263469

34273470
async def _read_group_metadata_v3(store: Store, path: str) -> GroupMetadata:
34283471
"""
3429-
Read group metadata or error
3472+
Read Zarr V3 group metadata.
34303473
"""
3431-
meta = await _read_metadata_v3(store=store, path=path)
3474+
try:
3475+
meta = await _read_metadata_v3(store=store, path=path)
3476+
except NodeNotFoundError as e:
3477+
# NodeNotFoundError is raised when neither array nor group metadata were found,
3478+
# but since this function is concerned with group metadata,
3479+
# it returns a more specific exception here.
3480+
msg = f"A group metadata document was not found in store {store!r} at path {path!r}."
3481+
raise GroupNotFoundError(msg) from e
34323482
if not isinstance(meta, GroupMetadata):
3433-
raise FileNotFoundError(f"Group metadata was not found in {store} at {path}")
3483+
msg = (f"Group metadata was not found in store {store!r} at path {path!r}. ",)
3484+
"An array metadata document was found there instead."
3485+
raise GroupNotFoundError(msg)
34343486
return meta
34353487

34363488

0 commit comments

Comments
 (0)