|
3 | 3 | from abc import ABC, abstractmethod
|
4 | 4 | from asyncio import gather
|
5 | 5 | from itertools import starmap
|
6 |
| -from typing import TYPE_CHECKING, NamedTuple, Protocol, runtime_checkable |
| 6 | +from typing import TYPE_CHECKING, Protocol, runtime_checkable |
7 | 7 |
|
8 | 8 | if TYPE_CHECKING:
|
9 | 9 | from collections.abc import AsyncGenerator, AsyncIterator, Iterable
|
10 | 10 | from types import TracebackType
|
11 | 11 | from typing import Any, Self, TypeAlias
|
12 | 12 |
|
13 | 13 | from zarr.core.buffer import Buffer, BufferPrototype
|
14 |
| - from zarr.core.common import AccessModeLiteral, BytesLike |
| 14 | + from zarr.core.common import BytesLike |
15 | 15 |
|
16 |
| -__all__ = ["AccessMode", "ByteGetter", "ByteSetter", "Store", "set_or_delete"] |
| 16 | +__all__ = ["ByteGetter", "ByteSetter", "Store", "set_or_delete"] |
17 | 17 |
|
18 | 18 | ByteRangeRequest: TypeAlias = tuple[int | None, int | None]
|
19 | 19 |
|
20 | 20 |
|
21 |
| -class AccessMode(NamedTuple): |
22 |
| - """Access mode flags.""" |
23 |
| - |
24 |
| - str: AccessModeLiteral |
25 |
| - readonly: bool |
26 |
| - overwrite: bool |
27 |
| - create: bool |
28 |
| - update: bool |
29 |
| - |
30 |
| - @classmethod |
31 |
| - def from_literal(cls, mode: AccessModeLiteral) -> Self: |
32 |
| - """ |
33 |
| - Create an AccessMode instance from a literal. |
34 |
| -
|
35 |
| - Parameters |
36 |
| - ---------- |
37 |
| - mode : AccessModeLiteral |
38 |
| - One of 'r', 'r+', 'w', 'w-', 'a'. |
39 |
| -
|
40 |
| - Returns |
41 |
| - ------- |
42 |
| - AccessMode |
43 |
| - The created instance. |
44 |
| -
|
45 |
| - Raises |
46 |
| - ------ |
47 |
| - ValueError |
48 |
| - If mode is not one of 'r', 'r+', 'w', 'w-', 'a'. |
49 |
| - """ |
50 |
| - if mode in ("r", "r+", "a", "w", "w-"): |
51 |
| - return cls( |
52 |
| - str=mode, |
53 |
| - readonly=mode == "r", |
54 |
| - overwrite=mode == "w", |
55 |
| - create=mode in ("a", "w", "w-"), |
56 |
| - update=mode in ("r+", "a"), |
57 |
| - ) |
58 |
| - raise ValueError("mode must be one of 'r', 'r+', 'w', 'w-', 'a'") |
59 |
| - |
60 |
| - |
61 | 21 | class Store(ABC):
|
62 | 22 | """
|
63 | 23 | Abstract base class for Zarr stores.
|
64 | 24 | """
|
65 | 25 |
|
66 |
| - _mode: AccessMode |
| 26 | + _read_only: bool |
67 | 27 | _is_open: bool
|
68 | 28 |
|
69 |
| - def __init__(self, *args: Any, mode: AccessModeLiteral = "r", **kwargs: Any) -> None: |
| 29 | + def __init__(self, *, read_only: bool = False) -> None: |
70 | 30 | self._is_open = False
|
71 |
| - self._mode = AccessMode.from_literal(mode) |
| 31 | + self._read_only = read_only |
72 | 32 |
|
73 | 33 | @classmethod
|
74 | 34 | async def open(cls, *args: Any, **kwargs: Any) -> Self:
|
@@ -112,81 +72,60 @@ async def _open(self) -> None:
|
112 | 72 | ------
|
113 | 73 | ValueError
|
114 | 74 | If the store is already open.
|
115 |
| - FileExistsError |
116 |
| - If ``mode='w-'`` and the store already exists. |
117 |
| -
|
118 |
| - Notes |
119 |
| - ----- |
120 |
| - * When ``mode='w'`` and the store already exists, it will be cleared. |
121 | 75 | """
|
122 | 76 | if self._is_open:
|
123 | 77 | raise ValueError("store is already open")
|
124 |
| - if self.mode.str == "w": |
125 |
| - await self.clear() |
126 |
| - elif self.mode.str == "w-" and not await self.empty(): |
127 |
| - raise FileExistsError("Store already exists") |
128 | 78 | self._is_open = True
|
129 | 79 |
|
130 | 80 | async def _ensure_open(self) -> None:
|
131 | 81 | """Open the store if it is not already open."""
|
132 | 82 | if not self._is_open:
|
133 | 83 | await self._open()
|
134 | 84 |
|
135 |
| - @abstractmethod |
136 |
| - async def empty(self) -> bool: |
| 85 | + async def is_empty(self, prefix: str) -> bool: |
137 | 86 | """
|
138 |
| - Check if the store is empty. |
| 87 | + Check if the directory is empty. |
| 88 | +
|
| 89 | + Parameters |
| 90 | + ---------- |
| 91 | + prefix : str |
| 92 | + Prefix of keys to check. |
139 | 93 |
|
140 | 94 | Returns
|
141 | 95 | -------
|
142 | 96 | bool
|
143 | 97 | True if the store is empty, False otherwise.
|
144 | 98 | """
|
145 |
| - ... |
| 99 | + if not self.supports_listing: |
| 100 | + raise NotImplementedError |
| 101 | + if prefix != "" and not prefix.endswith("/"): |
| 102 | + prefix += "/" |
| 103 | + async for _ in self.list_prefix(prefix): |
| 104 | + return False |
| 105 | + return True |
146 | 106 |
|
147 |
| - @abstractmethod |
148 | 107 | async def clear(self) -> None:
|
149 | 108 | """
|
150 | 109 | Clear the store.
|
151 | 110 |
|
152 | 111 | Remove all keys and values from the store.
|
153 | 112 | """
|
154 |
| - ... |
155 |
| - |
156 |
| - @abstractmethod |
157 |
| - def with_mode(self, mode: AccessModeLiteral) -> Self: |
158 |
| - """ |
159 |
| - Return a new store of the same type pointing to the same location with a new mode. |
160 |
| -
|
161 |
| - The returned Store is not automatically opened. Call :meth:`Store.open` before |
162 |
| - using. |
163 |
| -
|
164 |
| - Parameters |
165 |
| - ---------- |
166 |
| - mode : AccessModeLiteral |
167 |
| - The new mode to use. |
168 |
| -
|
169 |
| - Returns |
170 |
| - ------- |
171 |
| - store |
172 |
| - A new store of the same type with the new mode. |
173 |
| -
|
174 |
| - Examples |
175 |
| - -------- |
176 |
| - >>> writer = zarr.store.MemoryStore(mode="w") |
177 |
| - >>> reader = writer.with_mode("r") |
178 |
| - """ |
179 |
| - ... |
| 113 | + if not self.supports_deletes: |
| 114 | + raise NotImplementedError |
| 115 | + if not self.supports_listing: |
| 116 | + raise NotImplementedError |
| 117 | + self._check_writable() |
| 118 | + await self.delete_dir("") |
180 | 119 |
|
181 | 120 | @property
|
182 |
| - def mode(self) -> AccessMode: |
183 |
| - """Access mode of the store.""" |
184 |
| - return self._mode |
| 121 | + def read_only(self) -> bool: |
| 122 | + """Is the store read-only?""" |
| 123 | + return self._read_only |
185 | 124 |
|
186 | 125 | def _check_writable(self) -> None:
|
187 | 126 | """Raise an exception if the store is not writable."""
|
188 |
| - if self.mode.readonly: |
189 |
| - raise ValueError("store mode does not support writing") |
| 127 | + if self.read_only: |
| 128 | + raise ValueError("store was opened in read-only mode and does not support writing") |
190 | 129 |
|
191 | 130 | @abstractmethod
|
192 | 131 | def __eq__(self, value: object) -> bool:
|
@@ -385,7 +324,7 @@ async def delete_dir(self, prefix: str) -> None:
|
385 | 324 | if not self.supports_listing:
|
386 | 325 | raise NotImplementedError
|
387 | 326 | self._check_writable()
|
388 |
| - if not prefix.endswith("/"): |
| 327 | + if prefix != "" and not prefix.endswith("/"): |
389 | 328 | prefix += "/"
|
390 | 329 | async for key in self.list_prefix(prefix):
|
391 | 330 | await self.delete(key)
|
|
0 commit comments