|
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