diff --git a/src/zarr/api/asynchronous.py b/src/zarr/api/asynchronous.py index 680433565..cd8c3543c 100644 --- a/src/zarr/api/asynchronous.py +++ b/src/zarr/api/asynchronous.py @@ -396,12 +396,16 @@ async def save_array( mode = kwargs.pop("mode", None) store_path = await make_store_path(store, path=path, mode=mode, storage_options=storage_options) + if np.isscalar(arr): + arr = np.array(arr) + shape = arr.shape + chunks = getattr(arr, "chunks", None) # for array-likes with chunks attribute new = await AsyncArray.create( store_path, zarr_format=zarr_format, - shape=arr.shape, + shape=shape, dtype=arr.dtype, - chunks=arr.shape, + chunks=chunks, **kwargs, ) await new.setitem(slice(None), arr) diff --git a/src/zarr/core/group.py b/src/zarr/core/group.py index 6e54b7ec9..46f37700e 100644 --- a/src/zarr/core/group.py +++ b/src/zarr/core/group.py @@ -600,6 +600,23 @@ def from_dict( store_path=store_path, ) + async def setitem(self, key: str, value: Any) -> None: + """Fastpath for creating a new array + + New arrays will be created with default array settings for the array type. + + Parameters + ---------- + key : str + Array name + value : array-like + Array data + """ + path = self.store_path / key + await async_api.save_array( + store=path, arr=value, zarr_format=self.metadata.zarr_format, exists_ok=True + ) + async def getitem( self, key: str, @@ -1394,8 +1411,11 @@ def __len__(self) -> int: return self.nmembers() def __setitem__(self, key: str, value: Any) -> None: - """__setitem__ is not supported in v3""" - raise NotImplementedError + """Fastpath for creating a new array. + + New arrays will be created using default settings for the array type. + """ + self._sync(self._async_group.setitem(key, value)) def __repr__(self) -> str: return f"" diff --git a/src/zarr/storage/zip.py b/src/zarr/storage/zip.py index 204a381bd..d9e1aa130 100644 --- a/src/zarr/storage/zip.py +++ b/src/zarr/storage/zip.py @@ -219,7 +219,10 @@ async def set_if_not_exists(self, key: str, value: Buffer) -> None: async def delete(self, key: str) -> None: # docstring inherited - raise NotImplementedError + # 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 await self.exists(key): + raise NotImplementedError async def exists(self, key: str) -> bool: # docstring inherited diff --git a/tests/test_group.py b/tests/test_group.py index 21e4ef4e5..bcdc6ff0d 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -427,8 +427,25 @@ def test_group_setitem(store: Store, zarr_format: ZarrFormat) -> None: Test the `Group.__setitem__` method. """ group = Group.from_store(store, zarr_format=zarr_format) - with pytest.raises(NotImplementedError): - group["key"] = 10 + arr = np.ones((2, 4)) + group["key"] = arr + assert list(group.array_keys()) == ["key"] + assert group["key"].shape == (2, 4) + np.testing.assert_array_equal(group["key"][:], arr) + + if store.supports_deletes: + key = "key" + else: + # overwriting with another array requires deletes + # for stores that don't support this, we just use a new key + key = "key2" + + # overwrite with another array + arr = np.zeros((3, 5)) + group[key] = arr + assert key in list(group.array_keys()) + assert group[key].shape == (3, 5) + np.testing.assert_array_equal(group[key], arr) def test_group_contains(store: Store, zarr_format: ZarrFormat) -> None: