diff --git a/src/zarr/api/asynchronous.py b/src/zarr/api/asynchronous.py index 285d777258..2c0be356bd 100644 --- a/src/zarr/api/asynchronous.py +++ b/src/zarr/api/asynchronous.py @@ -326,7 +326,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) @@ -1252,13 +1256,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 f2c88c508b..f77d55cc17 100644 --- a/src/zarr/core/array.py +++ b/src/zarr/core/array.py @@ -871,6 +871,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. @@ -881,6 +882,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 ------- @@ -898,7 +901,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: diff --git a/tests/test_array.py b/tests/test_array.py index 5c3c556dfb..969fb31ac4 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -767,6 +767,43 @@ def test_resize_2d(store: MemoryStore, zarr_format: ZarrFormat) -> None: assert new_shape == result.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 + + 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)