Skip to content

Commit cb470d4

Browse files
committed
store -> readonly
1 parent 89e8539 commit cb470d4

30 files changed

+244
-381
lines changed

docs/guide/storage.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Storage
22
=======
33

44
Zarr-Python supports multiple storage backends, including: local file systems,
5-
Zip files, remote stores via ``fspec`` (S3, HTTP, etc.), and in-memory stores. In
5+
Zip files, remote stores via ``fsspec`` (S3, HTTP, etc.), and in-memory stores. In
66
Zarr-Python 3, stores must implement the abstract store API from
77
:class:`zarr.abc.store.Store`.
88

@@ -19,9 +19,9 @@ to Zarr's top level API will result in the store being created automatically.
1919
.. code-block:: python
2020
2121
>>> import zarr
22-
>>> zarr.open("data/foo/bar", mode="r") # implicitly creates a LocalStore
22+
>>> zarr.open("data/foo/bar", mode="r") # implicitly creates a read-only LocalStore
2323
<Group file://data/foo/bar>
24-
>>> zarr.open("s3://foo/bar", mode="r") # implicitly creates a RemoteStore
24+
>>> zarr.open("s3://foo/bar", mode="r") # implicitly creates a read-only RemoteStore
2525
<Group s3://foo/bar>
2626
>>> data = {}
2727
>>> zarr.open(data, mode="w") # implicitly creates a MemoryStore
@@ -43,7 +43,7 @@ filesystem.
4343
.. code-block:: python
4444
4545
>>> import zarr
46-
>>> store = zarr.storage.LocalStore("data/foo/bar", mode="r")
46+
>>> store = zarr.storage.LocalStore("data/foo/bar", readonly=True)
4747
>>> zarr.open(store=store)
4848
<Group file://data/foo/bar>
4949
@@ -72,7 +72,7 @@ that implements the `AbstractFileSystem` API,
7272
.. code-block:: python
7373
7474
>>> import zarr
75-
>>> store = zarr.storage.RemoteStore.from_url("gs://foo/bar", mode="r")
75+
>>> store = zarr.storage.RemoteStore.from_url("gs://foo/bar", readonly=True)
7676
>>> zarr.open(store=store)
7777
<Array <RemoteStore(GCSFileSystem, foo/bar)> shape=(10, 20) dtype=float32>
7878
@@ -86,7 +86,7 @@ Zarr data (metadata and chunks) to a dictionary.
8686
8787
>>> import zarr
8888
>>> data = {}
89-
>>> store = zarr.storage.MemoryStore(data, mode="w")
89+
>>> store = zarr.storage.MemoryStore(data)
9090
>>> zarr.open(store=store, shape=(2, ))
9191
<Array memory://4943638848 shape=(2,) dtype=float64>
9292

src/zarr/abc/store.py

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from abc import ABC, abstractmethod
44
from asyncio import gather
55
from itertools import starmap
6-
from typing import TYPE_CHECKING, Literal, Protocol, runtime_checkable
6+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
77

88
if TYPE_CHECKING:
99
from collections.abc import AsyncGenerator, Iterable
@@ -13,24 +13,22 @@
1313
from zarr.core.buffer import Buffer, BufferPrototype
1414
from zarr.core.common import BytesLike
1515

16-
__all__ = ["StoreAccessMode", "ByteGetter", "ByteSetter", "Store", "set_or_delete"]
16+
__all__ = ["ByteGetter", "ByteSetter", "Store", "set_or_delete"]
1717

1818
ByteRangeRequest: TypeAlias = tuple[int | None, int | None]
19-
StoreAccessMode = Literal["r", "w"]
2019

2120

2221
class Store(ABC):
2322
"""
2423
Abstract base class for Zarr stores.
2524
"""
2625

27-
_mode: StoreAccessMode
26+
_readonly: bool
2827
_is_open: bool
2928

30-
def __init__(self, *args: Any, mode: StoreAccessMode = "r", **kwargs: Any) -> None:
29+
def __init__(self, *, readonly: bool = False) -> None:
3130
self._is_open = False
32-
assert mode in ("r", "w")
33-
self._mode = mode
31+
self._readonly = readonly
3432

3533
@classmethod
3634
async def open(cls, *args: Any, **kwargs: Any) -> Self:
@@ -99,7 +97,7 @@ async def empty(self, prefix: str = "") -> bool:
9997
True if the store is empty, False otherwise.
10098
"""
10199

102-
if not prefix.endswith("/"):
100+
if prefix and not prefix.endswith("/"):
103101
prefix += "/"
104102
async for _ in self.list_prefix(prefix):
105103
return False
@@ -114,45 +112,15 @@ async def clear(self) -> None:
114112
async for key in self.list():
115113
await self.delete(key)
116114

117-
@abstractmethod
118-
def with_mode(self, mode: StoreAccessMode) -> Self:
119-
"""
120-
Return a new store of the same type pointing to the same location with a new mode.
121-
122-
The returned Store is not automatically opened. Call :meth:`Store.open` before
123-
using.
124-
125-
Parameters
126-
----------
127-
mode : StoreAccessMode
128-
The new mode to use.
129-
130-
Returns
131-
-------
132-
store
133-
A new store of the same type with the new mode.
134-
135-
Examples
136-
--------
137-
>>> writer = zarr.store.MemoryStore(mode="w")
138-
>>> reader = writer.with_mode("r")
139-
"""
140-
...
141-
142-
@property
143-
def mode(self) -> StoreAccessMode:
144-
"""Access mode of the store."""
145-
return self._mode
146-
147115
@property
148116
def readonly(self) -> bool:
149117
"""Is the store read-only?"""
150-
return self.mode == "r"
118+
return self._readonly
151119

152120
def _check_writable(self) -> None:
153121
"""Raise an exception if the store is not writable."""
154-
if self.mode != "w":
155-
raise ValueError(f"store mode ({self.mode}) does not support writing")
122+
if self.readonly:
123+
raise ValueError("store was opened in read-only mode and does not support writing")
156124

157125
@abstractmethod
158126
def __eq__(self, value: object) -> bool:
@@ -347,20 +315,6 @@ async def delete_dir(self, prefix: str) -> None:
347315
async for key in self.list_prefix(prefix):
348316
await self.delete(key)
349317

350-
async def delete_dir(self, prefix: str) -> None:
351-
"""
352-
Remove all keys and prefixes in the store that begin with a given prefix.
353-
"""
354-
if not self.supports_deletes:
355-
raise NotImplementedError
356-
if not self.supports_listing:
357-
raise NotImplementedError
358-
self._check_writable()
359-
if not prefix.endswith("/"):
360-
prefix += "/"
361-
async for key in self.list_prefix(prefix):
362-
await self.delete(key)
363-
364318
def close(self) -> None:
365319
"""Close the store."""
366320
self._is_open = False

src/zarr/api/asynchronous.py

Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
from collections.abc import Iterable
3131

3232
from zarr.abc.codec import Codec
33-
from zarr.abc.store import StoreAccessMode
34-
from zarr.core.buffer import NDArrayLike
3533
from zarr.core.chunk_key_encodings import ChunkKeyEncoding
3634

3735
# TODO: this type could use some more thought
@@ -66,30 +64,9 @@
6664
"zeros_like",
6765
]
6866

69-
# Persistence mode: 'r' means read only (must exist); 'r+' means
70-
# read/write (must exist); 'a' means read/write (create if doesn't
71-
# exist); 'w' means create (overwrite if exists); 'w-' means create
72-
# (fail if exists).
73-
persistence_to_store_modes: dict[AccessModeLiteral, StoreAccessMode] = {
74-
"r": "r",
75-
"r+": "w",
76-
"a": "w",
77-
"w": "w",
78-
"w-": "w",
79-
}
80-
81-
82-
def _handle_store_mode(mode: AccessModeLiteral | None) -> StoreAccessMode:
83-
if mode is None:
84-
return "r"
85-
else:
86-
return persistence_to_store_modes[mode]
87-
8867

8968
def _infer_exists_ok(mode: AccessModeLiteral) -> bool:
90-
if mode in ("a", "r+", "w"):
91-
return True
92-
return False
69+
return mode in ("a", "r+", "w")
9370

9471

9572
def _get_shape_chunks(a: ArrayLike | Any) -> tuple[ChunkCoords | None, ChunkCoords | None]:
@@ -313,11 +290,7 @@ async def open(
313290
"""
314291
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
315292

316-
store_mode = _handle_store_mode(mode)
317-
store_path = await make_store_path(
318-
store, mode=store_mode, path=path, storage_options=storage_options
319-
)
320-
await store_path._init(mode=mode)
293+
store_path = await make_store_path(store, mode=mode, path=path, storage_options=storage_options)
321294

322295
# TODO: the mode check below seems wrong!
323296
if "shape" not in kwargs and mode in {"a", "r", "r+"}:
@@ -427,11 +400,7 @@ async def save_array(
427400
raise TypeError("arr argument must be numpy or other NDArrayLike array")
428401

429402
mode = kwargs.pop("mode", "a")
430-
store_mode = _handle_store_mode(mode)
431-
store_path = await make_store_path(
432-
store, path=path, mode=store_mode, storage_options=storage_options
433-
)
434-
await store_path._init(mode=mode)
403+
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
435404
if np.isscalar(arr):
436405
arr = np.array(arr)
437406
shape = arr.shape
@@ -626,11 +595,7 @@ async def group(
626595
mode = "w"
627596
else:
628597
mode = "r+"
629-
store_mode = _handle_store_mode(mode)
630-
store_path = await make_store_path(
631-
store, path=path, mode=store_mode, storage_options=storage_options
632-
)
633-
await store_path._init(mode=mode)
598+
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
634599

635600
if chunk_store is not None:
636601
warnings.warn("chunk_store is not yet implemented", RuntimeWarning, stacklevel=2)
@@ -743,12 +708,8 @@ async def open_group(
743708
if chunk_store is not None:
744709
warnings.warn("chunk_store is not yet implemented", RuntimeWarning, stacklevel=2)
745710

746-
store_mode = _handle_store_mode(mode)
747711
exists_ok = _infer_exists_ok(mode)
748-
store_path = await make_store_path(
749-
store, mode=store_mode, storage_options=storage_options, path=path
750-
)
751-
await store_path._init(mode=mode)
712+
store_path = await make_store_path(store, mode=mode, storage_options=storage_options, path=path)
752713

753714
if attributes is None:
754715
attributes = {}
@@ -931,11 +892,7 @@ async def create(
931892
mode = kwargs.pop("mode", None)
932893
if mode is None:
933894
mode = "a"
934-
store_mode = _handle_store_mode(mode)
935-
store_path = await make_store_path(
936-
store, path=path, mode=store_mode, storage_options=storage_options
937-
)
938-
await store_path._init(mode)
895+
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
939896
return await AsyncArray.create(
940897
store_path,
941898
shape=shape,
@@ -1122,10 +1079,7 @@ async def open_array(
11221079
"""
11231080

11241081
mode = kwargs.pop("mode", None)
1125-
store_mode = _handle_store_mode(mode)
1126-
store_path = await make_store_path(
1127-
store, path=path, mode=store_mode, storage_options=storage_options
1128-
)
1082+
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
11291083

11301084
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
11311085

0 commit comments

Comments
 (0)