diff --git a/zarr/storage.py b/zarr/storage.py index f6903d29b2..2d717b68a8 100644 --- a/zarr/storage.py +++ b/zarr/storage.py @@ -1831,7 +1831,12 @@ def __exit__(self, *args): def __getitem__(self, key): with self.mutex: with self.zf.open(key) as f: # will raise KeyError - return f.read() + data = f.read() + + if data: + return data + else: + raise KeyError("Key not found") def __setitem__(self, key, value): if self.mode == "r": @@ -1852,7 +1857,23 @@ def __setitem__(self, key, value): self.zf.writestr(keyinfo, value) def __delitem__(self, key): - raise NotImplementedError + with self.mutex: + try: + self.zf.getinfo(key) + except KeyError: + raise KeyError("Cannot delete a non-existent key") + 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 # MS-DOS directory flag + else: + keyinfo.external_attr = 0o644 << 16 # ?rw-r--r-- + + self.zf.writestr(keyinfo, b"") def __eq__(self, other): return ( @@ -1864,7 +1885,8 @@ def __eq__(self, other): def keylist(self): with self.mutex: - return sorted(self.zf.namelist()) + namelist = [key for key in self.zf.namelist() if self.zf.getinfo(key) != b""] + return sorted(namelist) def keys(self): yield from self.keylist() @@ -1878,7 +1900,11 @@ def __len__(self): def __contains__(self, key): try: with self.mutex: - self.zf.getinfo(key) + value = self.zf.getinfo(key) + + if value == b"": + raise KeyError + except KeyError: return False else: diff --git a/zarr/tests/test_storage.py b/zarr/tests/test_storage.py index 358d043ad6..797feb945e 100644 --- a/zarr/tests/test_storage.py +++ b/zarr/tests/test_storage.py @@ -217,6 +217,21 @@ def test_pop(self): store.close() + def test_delitem(self): + store = self.create_store() + foo = self.root + 'foo' + store[foo] = b'bar' + + del store[foo] + + with pytest.raises(KeyError): + store[foo] + # if foo in store: + # if isinstance(store, ZipStore): + # assert store[foo] == b'' + # else: + # assert foo not in store + def test_popitem(self): store = self.create_store() store[self.root + "foo"] = b"bar" @@ -1803,8 +1818,7 @@ def test_flush(self): store.flush() assert store[self.root + "foo"] == b"bar" store.close() - - store = self.ZipStoreClass("data/store.zip", mode="r") + store = self.ZipStoreClass('data/store.zip', mode='r') store.flush() # no-op def test_context_manager(self): @@ -1816,9 +1830,9 @@ def test_context_manager(self): def test_pop(self): # override because not implemented store = self.create_store() - store[self.root + "foo"] = b"bar" - with pytest.raises(NotImplementedError): - store.pop(self.root + "foo") + store[self.root + 'foo'] = b'bar' + store.pop(self.root + 'foo') + assert store[self.root + 'foo'] == b"" def test_popitem(self): # override because not implemented