From 2c5ebac78ab4a188833d9c382f7ea8cfb41fa95b Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Wed, 26 Mar 2025 15:08:19 -0400 Subject: [PATCH 1/4] test: add test for re-open and write_empty_chunks --- tests/test_array.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_array.py b/tests/test_array.py index efcf8a6bf9..70df76eb68 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -746,6 +746,44 @@ def test_resize_2d(store: MemoryStore, zarr_format: ZarrFormat) -> None: assert new_shape == z[:].shape +@pytest.mark.parametrize("store", ["local"], indirect=True) +@pytest.mark.parametrize("open", ["open", "open_array"]) +def test_append_config_passed(store: LocalStore, open: str, zarr_format: ZarrFormat) -> None: + z = zarr.create_array( + store=store, + name="test", + shape=(2,), + dtype=int, + fill_value=0, + chunks=(1,), + config={"write_empty_chunks": True}, + overwrite=True, + zarr_format=zarr_format, + ) + z[:] = 0 + print(store) + + def assert_correct_files_written(expected: list[str]) -> None: + """Helper to compare written files""" + if zarr_format == 2: + actual = [f.name for f in store.root.rglob("test/*")] + else: + actual = [f.name for f in store.root.rglob("test/c/*")] + actual = [f for f in actual if f not in [".zattrs", ".zarray", "zarr.json"]] + assert sorted(expected) == sorted(actual) + + assert_correct_files_written(["0", "1"]) + + # parametrized over open and open_array + z = getattr(zarr, open)(store, path="test", config={"write_empty_chunks": True}, fill_value=0) + z.resize((4,)) + assert_correct_files_written(["0", "1"]) + z[2:] = 0 + assert_correct_files_written(["0", "1", "2", "3"]) + z[:] = 0 + assert_correct_files_written(["0", "1", "2", "3"]) + + @pytest.mark.parametrize("store", ["memory"], indirect=True) def test_append_1d(store: MemoryStore, zarr_format: ZarrFormat) -> None: a = np.arange(105) From 43c9c7e1f851314f92b7e5d214e7cc49bed9d8aa Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Wed, 26 Mar 2025 15:09:47 -0400 Subject: [PATCH 2/4] fix: respect config when appending to array from zarr.open --- src/zarr/api/asynchronous.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/zarr/api/asynchronous.py b/src/zarr/api/asynchronous.py index 6059893920..2a96432a77 100644 --- a/src/zarr/api/asynchronous.py +++ b/src/zarr/api/asynchronous.py @@ -324,7 +324,11 @@ async def open( zarr_format = _metadata_dict["zarr_format"] is_v3_array = zarr_format == 3 and _metadata_dict.get("node_type") == "array" if is_v3_array or zarr_format == 2: - return AsyncArray(store_path=store_path, metadata=_metadata_dict) + return AsyncArray( + store_path=store_path, + metadata=_metadata_dict, + config=kwargs.pop("config", None), + ) except (AssertionError, FileNotFoundError, NodeTypeValidationError): pass return await open_group(store=store_path, zarr_format=zarr_format, mode=mode, **kwargs) From 0c81853296527f7dc169ea765aa82fb60e15b88b Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Wed, 26 Mar 2025 15:15:26 -0400 Subject: [PATCH 3/4] fix: respect config when appending to array from zarr.open_array --- src/zarr/api/asynchronous.py | 15 +++++++++++++-- src/zarr/core/array.py | 5 ++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/zarr/api/asynchronous.py b/src/zarr/api/asynchronous.py index 2a96432a77..61c613adf2 100644 --- a/src/zarr/api/asynchronous.py +++ b/src/zarr/api/asynchronous.py @@ -1251,13 +1251,24 @@ async def open_array( zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format) + config: ArrayConfigParams = kwargs.get("config", {}) if "order" in kwargs: _warn_order_kwarg() if "write_empty_chunks" in kwargs: - _warn_write_empty_chunks_kwarg() + if len(config) != 0: + msg = ( + "Both write_empty_chunks and config keyword arguments are set. " + "This is redundant. When both are set, write_empty_chunks will be ignored and " + "config will be used." + ) + warnings.warn(UserWarning(msg), stacklevel=1) + else: + _warn_write_empty_chunks_kwarg() + # don't pop as this is only used for AsyncArray.open + config["write_empty_chunks"] = kwargs["write_empty_chunks"] try: - return await AsyncArray.open(store_path, zarr_format=zarr_format) + return await AsyncArray.open(store_path, zarr_format=zarr_format, config=config) except FileNotFoundError: if not store_path.read_only and mode in _CREATE_MODES: overwrite = _infer_overwrite(mode) diff --git a/src/zarr/core/array.py b/src/zarr/core/array.py index 0e03cbcabb..23887d6e6e 100644 --- a/src/zarr/core/array.py +++ b/src/zarr/core/array.py @@ -869,6 +869,7 @@ async def open( cls, store: StoreLike, zarr_format: ZarrFormat | None = 3, + config: ArrayConfigLike | None = None, ) -> AsyncArray[ArrayV3Metadata] | AsyncArray[ArrayV2Metadata]: """ Async method to open an existing Zarr array from a given store. @@ -879,6 +880,8 @@ async def open( The store containing the Zarr array. zarr_format : ZarrFormat | None, optional The Zarr format version (default is 3). + config : ArrayConfigLike, optional + Runtime configuration for the array. Returns ------- @@ -896,7 +899,7 @@ async def open( metadata_dict = await get_array_metadata(store_path, zarr_format=zarr_format) # TODO: remove this cast when we have better type hints _metadata_dict = cast(ArrayV3MetadataDict, metadata_dict) - return cls(store_path=store_path, metadata=_metadata_dict) + return cls(store_path=store_path, metadata=_metadata_dict, config=config) @property def store(self) -> Store: From 2c29cf65d98bcbce0dd77607bb360a484728373d Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Wed, 26 Mar 2025 15:22:11 -0400 Subject: [PATCH 4/4] test: remove extra print --- tests/test_array.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_array.py b/tests/test_array.py index 70df76eb68..ad32c26f66 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -761,7 +761,6 @@ def test_append_config_passed(store: LocalStore, open: str, zarr_format: ZarrFor zarr_format=zarr_format, ) z[:] = 0 - print(store) def assert_correct_files_written(expected: list[str]) -> None: """Helper to compare written files"""