Skip to content

Commit 481550a

Browse files
authored
Error on invalid store mode (#3068)
* Error on invalid store mode * Fix tests * Add release note * Fix test
1 parent 18c4a9e commit 481550a

File tree

5 files changed

+20
-8
lines changed

5 files changed

+20
-8
lines changed

changes/3068.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Trying to open an array with ``mode='r'`` when the store is not read-only now raises an error.

src/zarr/storage/_common.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import json
44
from pathlib import Path
5-
from typing import TYPE_CHECKING, Any, Literal
5+
from typing import TYPE_CHECKING, Any, Literal, Self
66

77
from zarr.abc.store import ByteRequest, Store
88
from zarr.core.buffer import Buffer, default_buffer_prototype
@@ -48,9 +48,7 @@ def read_only(self) -> bool:
4848
return self.store.read_only
4949

5050
@classmethod
51-
async def open(
52-
cls, store: Store, path: str, mode: AccessModeLiteral | None = None
53-
) -> StorePath:
51+
async def open(cls, store: Store, path: str, mode: AccessModeLiteral | None = None) -> Self:
5452
"""
5553
Open StorePath based on the provided mode.
5654
@@ -67,6 +65,9 @@ async def open(
6765
------
6866
FileExistsError
6967
If the mode is 'w-' and the store path already exists.
68+
ValueError
69+
If the mode is not "r" and the store is read-only, or
70+
if the mode is "r" and the store is not read-only.
7071
"""
7172

7273
await store._ensure_open()
@@ -78,6 +79,8 @@ async def open(
7879

7980
if store.read_only and mode != "r":
8081
raise ValueError(f"Store is read-only but mode is '{mode}'")
82+
if not store.read_only and mode == "r":
83+
raise ValueError(f"Store is not read-only but mode is '{mode}'")
8184

8285
match mode:
8386
case "w-":

tests/test_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def test_v2_and_v3_exist_at_same_path(store: Store) -> None:
171171
zarr.create_array(store, shape=(10,), dtype="uint8", zarr_format=2)
172172
msg = f"Both zarr.json (Zarr format 3) and .zarray (Zarr format 2) metadata objects exist at {store}. Zarr v3 will be used."
173173
with pytest.warns(UserWarning, match=re.escape(msg)):
174-
zarr.open(store=store, mode="r")
174+
zarr.open(store=store)
175175

176176

177177
@pytest.mark.parametrize("store", ["memory"], indirect=True)
@@ -1285,7 +1285,7 @@ def test_no_overwrite_open(tmp_path: Path, open_func: Callable, mode: str) -> No
12851285
existing_fpath = add_empty_file(tmp_path)
12861286

12871287
assert existing_fpath.exists()
1288-
with contextlib.suppress(FileExistsError, FileNotFoundError):
1288+
with contextlib.suppress(FileExistsError, FileNotFoundError, ValueError):
12891289
open_func(store=store, mode=mode)
12901290
if mode == "w":
12911291
assert not existing_fpath.exists()

tests/test_array.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,7 +1473,7 @@ async def test_name(store: Store, zarr_format: ZarrFormat, path: str | None) ->
14731473
for parent_path in parents:
14741474
# this will raise if these groups were not created
14751475
_ = await zarr.api.asynchronous.open_group(
1476-
store=store, path=parent_path, mode="r", zarr_format=zarr_format
1476+
store=store, path=parent_path, zarr_format=zarr_format
14771477
)
14781478

14791479

@@ -1661,7 +1661,7 @@ def test_roundtrip_numcodecs() -> None:
16611661

16621662
BYTES_CODEC = {"name": "bytes", "configuration": {"endian": "little"}}
16631663
# Read in the array again and check compressor config
1664-
root = zarr.open_group(store, mode="r")
1664+
root = zarr.open_group(store)
16651665
metadata = root["test"].metadata.to_dict()
16661666
expected = (*filters, BYTES_CODEC, *compressors)
16671667
assert metadata["codecs"] == expected

tests/test_store/test_core.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55
from _pytest.compat import LEGACY_PATH
66

7+
import zarr
78
from zarr import Group
89
from zarr.core.common import AccessModeLiteral, ZarrFormat
910
from zarr.storage import FsspecStore, LocalStore, MemoryStore, StoreLike, StorePath
@@ -251,3 +252,10 @@ def test_relativize_path_invalid() -> None:
251252
msg = f"The first component of {path} does not start with {prefix}."
252253
with pytest.raises(ValueError, match=msg):
253254
_relativize_path(path="a/b/c", prefix="b")
255+
256+
257+
def test_invalid_open_mode() -> None:
258+
store = MemoryStore()
259+
zarr.create((100,), store=store, zarr_format=2, path="a")
260+
with pytest.raises(ValueError, match="Store is not read-only but mode is 'r'"):
261+
zarr.open_array(store=store, path="a", zarr_format=2, mode="r")

0 commit comments

Comments
 (0)