Skip to content

Commit 77115ff

Browse files
committed
feature(group): add group setitem api
1 parent 85c9b5f commit 77115ff

File tree

4 files changed

+41
-7
lines changed

4 files changed

+41
-7
lines changed

src/zarr/api/asynchronous.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,12 +396,16 @@ async def save_array(
396396

397397
mode = kwargs.pop("mode", None)
398398
store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options)
399+
if np.isscalar(arr):
400+
arr = np.array(arr)
401+
shape = arr.shape
402+
chunks = getattr(arr, "chunks", shape) # for array-likes with chunks attribute
399403
new = await AsyncArray.create(
400404
store_path,
401405
zarr_format=zarr_format,
402-
shape=arr.shape,
406+
shape=shape,
403407
dtype=arr.dtype,
404-
chunks=arr.shape,
408+
chunks=chunks,
405409
**kwargs,
406410
)
407411
await new.setitem(slice(None), arr)

src/zarr/core/group.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,12 @@ def from_dict(
596596
store_path=store_path,
597597
)
598598

599+
async def setitem(self, key: str, value: Any) -> None:
600+
path = self.store_path / key
601+
await async_api.save_array(
602+
store=path, arr=value, zarr_format=self.metadata.zarr_format, exists_ok=True
603+
)
604+
599605
async def getitem(
600606
self,
601607
key: str,
@@ -1369,8 +1375,8 @@ def __len__(self) -> int:
13691375
return self.nmembers()
13701376

13711377
def __setitem__(self, key: str, value: Any) -> None:
1372-
"""__setitem__ is not supported in v3"""
1373-
raise NotImplementedError
1378+
"""Create a new array"""
1379+
self._sync(self._async_group.setitem(key, value))
13741380

13751381
def __repr__(self) -> str:
13761382
return f"<Group {self.store_path}>"

src/zarr/storage/zip.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,10 @@ async def set_if_not_exists(self, key: str, value: Buffer) -> None:
219219

220220
async def delete(self, key: str) -> None:
221221
# docstring inherited
222-
raise NotImplementedError
222+
# we choose to only raise NotImplementedError here if the key exists
223+
# this allows the array/group APIs to avoid the overhead of existence checks
224+
if await self.exists(key):
225+
raise NotImplementedError
223226

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

tests/v3/test_group.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,29 @@ def test_group_setitem(store: Store, zarr_format: ZarrFormat) -> None:
391391
Test the `Group.__setitem__` method.
392392
"""
393393
group = Group.from_store(store, zarr_format=zarr_format)
394-
with pytest.raises(NotImplementedError):
395-
group["key"] = 10
394+
arr = np.ones((2, 4))
395+
group["key"] = arr
396+
assert group["key"].shape == (2, 4)
397+
np.testing.assert_array_equal(group["key"][:], arr)
398+
399+
if store.supports_deletes:
400+
key = "key"
401+
else:
402+
# overwriting with another array requires deletes
403+
# for stores that don't support this, we just use a new key
404+
key = "key2"
405+
406+
# overwrite with another array
407+
arr = np.zeros((3, 5))
408+
group[key] = arr
409+
assert group[key].shape == (3, 5)
410+
np.testing.assert_array_equal(group[key], arr)
411+
412+
# overwrite with a scalar
413+
# separate bug!
414+
# group["key"] = 1.5
415+
# assert group["key"].shape == ()
416+
# assert group["key"][:] == 1
396417

397418

398419
def test_group_contains(store: Store, zarr_format: ZarrFormat) -> None:

0 commit comments

Comments
 (0)