diff --git a/src/zarr/storage/_zip.py b/src/zarr/storage/_zip.py index bbfe6c67aa..ab155a5d8b 100644 --- a/src/zarr/storage/_zip.py +++ b/src/zarr/storage/_zip.py @@ -241,11 +241,19 @@ async def delete_dir(self, prefix: str) -> None: async def delete(self, key: str) -> None: # docstring inherited - # we choose to only raise NotImplementedError here if the key exists - # this allows the array/group APIs to avoid the overhead of existence checks + # If key is present it is replaced by an empty byte literal as a way of representing it as deleted self._check_writable() if await self.exists(key): - raise NotImplementedError + keyinfo = zipfile.ZipInfo(filename=key, date_time=time.localtime(time.time())[:6]) + keyinfo.compress_type = self.compression + if keyinfo.filename[-1] == os.sep: + keyinfo.external_attr = 0o40775 << 16 # drwxrwxr-x + keyinfo.external_attr |= 0x10 + else: + keyinfo.external_attr = 0o644 << 16 # ?rw-r--r-- + + with self._lock: + self._zf.writestr(keyinfo, b"") async def exists(self, key: str) -> bool: # docstring inherited diff --git a/src/zarr/testing/store.py b/src/zarr/testing/store.py index 112f6261e9..e5dead6f5a 100644 --- a/src/zarr/testing/store.py +++ b/src/zarr/testing/store.py @@ -5,7 +5,7 @@ from abc import abstractmethod from typing import TYPE_CHECKING, Generic, TypeVar -from zarr.storage import WrapperStore +from zarr.storage import WrapperStore, ZipStore if TYPE_CHECKING: from typing import Any @@ -340,6 +340,15 @@ async def test_delete(self, store: S) -> None: await store.delete("foo/zarr.json") assert not await store.exists("foo/zarr.json") + async def test_delete_zip_store(self, store: S) -> None: + if not isinstance(store, ZipStore): + pytest.skip("store is not a ZipStore") + await store.set("foo/zarr.json", self.buffer_cls.from_bytes(b"bar")) + assert await store.exists("foo/zarr.json") + await store.delete("foo/zarr.json") + result = await store.get("foo/zarr.json", default_buffer_prototype()) + assert result == self.buffer_cls.from_bytes(b"") + async def test_delete_dir(self, store: S) -> None: if not store.supports_deletes: pytest.skip("store does not support deletes") diff --git a/tests/test_store/test_zip.py b/tests/test_store/test_zip.py index 839656108b..ce601549ac 100644 --- a/tests/test_store/test_zip.py +++ b/tests/test_store/test_zip.py @@ -83,8 +83,8 @@ def test_api_integration(self, store: ZipStore) -> None: # TODO: assigning an entire chunk to fill value ends up deleting the chunk which is not supported # a work around will be needed here. - with pytest.raises(NotImplementedError): - z[0:10, 0:10] = 99 + + z[0:10, 0:10] = 99 bar = root.create_group("bar", attributes={"hello": "world"}) assert "hello" in dict(bar.attrs)