Skip to content

Commit f1fae84

Browse files
committed
Implement soft delete for the zip store
Signed-off-by: asim <[email protected]>
1 parent 15fcc89 commit f1fae84

File tree

3 files changed

+21
-5
lines changed

3 files changed

+21
-5
lines changed

src/zarr/storage/_zip.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,16 @@ async def delete(self, key: str) -> None:
244244
# If key is present it is replaced by an empty byte literal as a way of representing it as deleted
245245
self._check_writable()
246246
if await self.exists(key):
247+
keyinfo = zipfile.ZipInfo(filename=key, date_time=time.localtime(time.time())[:6])
248+
keyinfo.compress_type = self.compression
249+
if keyinfo.filename[-1] == os.sep:
250+
keyinfo.external_attr = 0o40775 << 16 # drwxrwxr-x
251+
keyinfo.external_attr |= 0x10
252+
else:
253+
keyinfo.external_attr = 0o644 << 16 # ?rw-r--r--
254+
247255
with self._lock:
248-
empty_buffer = Buffer.from_bytes(b"")
249-
self._set(key, empty_buffer)
256+
self._zf.writestr(keyinfo, b"")
250257

251258
async def exists(self, key: str) -> bool:
252259
# docstring inherited

src/zarr/testing/store.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from abc import abstractmethod
66
from typing import TYPE_CHECKING, Generic, TypeVar
77

8-
from zarr.storage import WrapperStore
8+
from zarr.storage import WrapperStore, ZipStore
99

1010
if TYPE_CHECKING:
1111
from typing import Any
@@ -340,6 +340,15 @@ async def test_delete(self, store: S) -> None:
340340
await store.delete("foo/zarr.json")
341341
assert not await store.exists("foo/zarr.json")
342342

343+
async def test_delete_zip_store(self, store: S) -> None:
344+
if not isinstance(store, ZipStore):
345+
pytest.skip("store is not a ZipStore")
346+
await store.set("foo/zarr.json", self.buffer_cls.from_bytes(b"bar"))
347+
assert await store.exists("foo/zarr.json")
348+
await store.delete("foo/zarr.json")
349+
result = await store.get("foo/zarr.json", default_buffer_prototype())
350+
assert result == self.buffer_cls.from_bytes(b"")
351+
343352
async def test_delete_dir(self, store: S) -> None:
344353
if not store.supports_deletes:
345354
pytest.skip("store does not support deletes")

tests/test_store/test_zip.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ def test_api_integration(self, store: ZipStore) -> None:
8383

8484
# TODO: assigning an entire chunk to fill value ends up deleting the chunk which is not supported
8585
# a work around will be needed here.
86-
with pytest.raises(NotImplementedError):
87-
z[0:10, 0:10] = 99
86+
87+
z[0:10, 0:10] = 99
8888

8989
bar = root.create_group("bar", attributes={"hello": "world"})
9090
assert "hello" in dict(bar.attrs)

0 commit comments

Comments
 (0)