From 93b76fd8e6d14689c9280a1d6016d99df2d7679e Mon Sep 17 00:00:00 2001 From: NIK-TIGER-BILL Date: Sun, 7 Jun 2026 23:50:23 +0000 Subject: [PATCH] fix: allow opening non-existent ZipStore in read mode Fixes zarr-developers/zarr-python#2450 Signed-off-by: NIK-TIGER-BILL --- src/zarr/storage/_zip.py | 12 ++++++++++++ tests/test_store/test_zip.py | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/zarr/storage/_zip.py b/src/zarr/storage/_zip.py index 897797e999..c047cfe0a3 100644 --- a/src/zarr/storage/_zip.py +++ b/src/zarr/storage/_zip.py @@ -94,6 +94,18 @@ def _sync_open(self) -> None: self._lock = threading.RLock() + if self._zmode == "r" and not self.path.exists(): + # zipfile.ZipFile requires the file to exist in read mode, + # but other stores can be opened in read mode without IO. + # create an empty zip file so that the store can be opened. + with zipfile.ZipFile( + self.path, + mode="w", + compression=self.compression, + allowZip64=self.allowZip64, + ): + pass + self._zf = zipfile.ZipFile( self.path, mode=self._zmode, diff --git a/tests/test_store/test_zip.py b/tests/test_store/test_zip.py index be51bcedcb..8b1e5dfb63 100644 --- a/tests/test_store/test_zip.py +++ b/tests/test_store/test_zip.py @@ -177,3 +177,21 @@ async def test_move(self, tmp_path: Path) -> None: assert destination.exists() assert not origin.exists() assert np.array_equal(array[...], np.arange(10)) + + async def test_fresh_zip_store_read_mode(self, tmp_path: Path) -> None: + # See: https://github.com/zarr-developers/zarr-python/issues/2450 + # A fresh ZipStore should be openable in read mode without error. + zip_path = tmp_path / "fresh.zip" + assert not zip_path.exists() + + store = await ZipStore.open(path=zip_path, mode="r") + assert store._is_open + assert store.read_only + + keys = [k async for k in store.list()] + assert keys == [] + + assert await store.get("nonexistent", default_buffer_prototype()) is None + assert not await store.exists("nonexistent") + + store.close()