Skip to content

Commit acdb9ef

Browse files
committed
raise RuntimeError when attempting to set or get a closed store
1 parent 8dceef2 commit acdb9ef

File tree

9 files changed

+26
-25
lines changed

9 files changed

+26
-25
lines changed

src/zarr/abc/store.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ async def _open(self) -> None:
5252
raise FileExistsError("Store already exists")
5353
self._is_open = True
5454

55-
async def _ensure_open(self) -> None:
56-
if not self._is_open:
57-
await self._open()
58-
5955
@abstractmethod
6056
async def empty(self) -> bool: ...
6157

src/zarr/store/core.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ async def make_store_path(
8080
elif isinstance(store_like, Store):
8181
if mode is not None:
8282
assert AccessMode.from_literal(mode) == store_like.mode
83-
await store_like._ensure_open()
8483
return StorePath(store_like)
8584
elif store_like is None:
8685
if mode is None:

src/zarr/store/local.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@ class LocalStore(Store):
7373
root: Path
7474

7575
def __init__(self, root: Path | str, *, mode: AccessModeLiteral = "r"):
76-
super().__init__(mode=mode)
7776
if isinstance(root, str):
7877
root = Path(root)
7978
assert isinstance(root, Path)
8079
self.root = root
80+
super().__init__(mode=mode)
8181

8282
async def clear(self) -> None:
8383
self._check_writable()

src/zarr/store/memory.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ def __init__(
2323
*,
2424
mode: AccessModeLiteral = "r",
2525
):
26-
super().__init__(mode=mode)
2726
self._store_dict = store_dict or {}
27+
super().__init__(mode=mode)
2828

2929
async def empty(self) -> bool:
3030
return not self._store_dict
@@ -45,7 +45,7 @@ async def get(
4545
byte_range: tuple[int | None, int | None] | None = None,
4646
) -> Buffer | None:
4747
if not self._is_open:
48-
await self._open()
48+
raise RuntimeError("Store is closed. Cannot `get` from a closed store.")
4949
assert isinstance(key, str)
5050
try:
5151
value = self._store_dict[key]
@@ -71,7 +71,7 @@ async def exists(self, key: str) -> bool:
7171

7272
async def set(self, key: str, value: Buffer, byte_range: tuple[int, int] | None = None) -> None:
7373
if not self._is_open:
74-
await self._open()
74+
raise RuntimeError("Store is closed. Cannot `get` from a closed store.")
7575
self._check_writable()
7676
assert isinstance(key, str)
7777
if not isinstance(value, Buffer):

src/zarr/store/remote.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def __init__(
5050
storage_options: passed on to fsspec to make the filesystem instance. If url is a UPath,
5151
this must not be used.
5252
"""
53-
super().__init__(mode=mode)
53+
5454
if isinstance(url, str):
5555
self._url = url.rstrip("/")
5656
self._fs, _path = fsspec.url_to_fs(url, **storage_options)
@@ -73,6 +73,7 @@ def __init__(
7373
# test instantiate file system
7474
if not self._fs.async_impl:
7575
raise TypeError("FileSystem needs to support async operations")
76+
super().__init__(mode=mode)
7677

7778
async def clear(self) -> None:
7879
try:

tests/v3/test_buffer.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
from typing import Any
4+
35
import numpy as np
46
import pytest
57

@@ -20,19 +22,19 @@
2022
)
2123

2224

23-
def test_nd_array_like(xp):
25+
def test_nd_array_like(xp: Any) -> None:
2426
ary = xp.arange(10)
2527
assert isinstance(ary, ArrayLike)
2628
assert isinstance(ary, NDArrayLike)
2729

2830

2931
@pytest.mark.asyncio
30-
async def test_async_array_prototype():
32+
async def test_async_array_prototype() -> None:
3133
"""Test the use of a custom buffer prototype"""
3234

3335
expect = np.zeros((9, 9), dtype="uint16", order="F")
3436
a = await AsyncArray.create(
35-
StorePath(StoreExpectingTestBuffer(mode="w")) / "test_async_array_prototype",
37+
StorePath(await StoreExpectingTestBuffer.open(mode="w")) / "test_async_array_prototype",
3638
shape=expect.shape,
3739
chunk_shape=(5, 5),
3840
dtype=expect.dtype,
@@ -53,10 +55,10 @@ async def test_async_array_prototype():
5355

5456

5557
@pytest.mark.asyncio
56-
async def test_codecs_use_of_prototype():
58+
async def test_codecs_use_of_prototype() -> None:
5759
expect = np.zeros((10, 10), dtype="uint16", order="F")
5860
a = await AsyncArray.create(
59-
StorePath(StoreExpectingTestBuffer(mode="w")) / "test_codecs_use_of_prototype",
61+
StorePath(await StoreExpectingTestBuffer.open(mode="w")) / "test_codecs_use_of_prototype",
6062
shape=expect.shape,
6163
chunk_shape=(5, 5),
6264
dtype=expect.dtype,
@@ -84,7 +86,7 @@ async def test_codecs_use_of_prototype():
8486
assert np.array_equal(expect, got)
8587

8688

87-
def test_numpy_buffer_prototype():
89+
def test_numpy_buffer_prototype() -> None:
8890
buffer = numpy_buffer_prototype().buffer.create_zero_length()
8991
ndbuffer = numpy_buffer_prototype().nd_buffer.create(shape=(1, 2), dtype=np.dtype("int64"))
9092
assert isinstance(buffer.as_array_like(), np.ndarray)

tests/v3/test_config.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
register_ndbuffer,
2828
register_pipeline,
2929
)
30+
from zarr.sync import sync
3031
from zarr.testing.buffer import (
3132
NDBufferUsingTestNDArrayLike,
3233
StoreExpectingTestBuffer,
@@ -191,11 +192,11 @@ def test_config_ndbuffer_implementation(store):
191192
assert isinstance(got, TestNDArrayLike)
192193

193194

194-
def test_config_buffer_implementation():
195+
def test_config_buffer_implementation() -> None:
195196
# has default value
196197
assert fully_qualified_name(get_buffer_class()) == config.defaults[0]["buffer"]
197-
198-
arr = zeros(shape=(100), store=StoreExpectingTestBuffer(mode="w"))
198+
store = sync(StoreExpectingTestBuffer.open(mode="w"))
199+
arr = zeros(shape=(100), store=store)
199200

200201
# AssertionError of StoreExpectingTestBuffer when not using my buffer
201202
with pytest.raises(AssertionError):
@@ -213,15 +214,15 @@ def test_config_buffer_implementation():
213214
data2d = np.arange(1000).reshape(100, 10)
214215
arr_sharding = zeros(
215216
shape=(100, 10),
216-
store=StoreExpectingTestBuffer(mode="w"),
217+
store=sync(StoreExpectingTestBuffer.open(mode="w")),
217218
codecs=[ShardingCodec(chunk_shape=(10, 10))],
218219
)
219220
arr_sharding[:] = data2d
220221
assert np.array_equal(arr_sharding[:], data2d)
221222

222223
arr_Crc32c = zeros(
223224
shape=(100, 10),
224-
store=StoreExpectingTestBuffer(mode="w"),
225+
store=sync(StoreExpectingTestBuffer.open(mode="w")),
225226
codecs=[BytesCodec(), Crc32cCodec()],
226227
)
227228
arr_Crc32c[:] = data2d

tests/v3/test_indexing.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@
2727
from zarr.registry import get_ndbuffer_class
2828
from zarr.store.core import StorePath
2929
from zarr.store.memory import MemoryStore
30+
from zarr.sync import sync
3031

3132

3233
@pytest.fixture
3334
def store() -> Iterator[StorePath]:
34-
yield StorePath(MemoryStore(mode="w"))
35+
yield StorePath(sync(MemoryStore.open(mode="w")))
3536

3637

3738
def zarr_array_from_numpy_array(
@@ -1838,4 +1839,5 @@ def test_indexing_after_close(store: StorePath) -> None:
18381839
a[:] = nparray
18391840
assert a[:] == value
18401841
store.store.close()
1841-
assert a[:] == value
1842+
with pytest.raises(RuntimeError, match="Store is closed"):
1843+
assert a[:] == value

tests/v3/test_store/test_memory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ def store_kwargs(
2323
return {"store_dict": request.param, "mode": "r+"}
2424

2525
@pytest.fixture(scope="function")
26-
def store(self, store_kwargs: str | None | dict[str, Buffer]) -> MemoryStore:
27-
return self.store_cls(**store_kwargs)
26+
async def store(self, store_kwargs: str | None | dict[str, Buffer]) -> MemoryStore:
27+
return await self.store_cls.open(**store_kwargs)
2828

2929
def test_store_repr(self, store: MemoryStore) -> None:
3030
assert str(store) == f"memory://{id(store._store_dict)}"

0 commit comments

Comments
 (0)