Skip to content
1 change: 1 addition & 0 deletions changes/3128.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `zarr.open` default for argument `mode` when `store` is `read_only`
9 changes: 7 additions & 2 deletions src/zarr/api/asynchronous.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import numpy.typing as npt
from typing_extensions import deprecated

from zarr.abc.store import Store
from zarr.core.array import (
Array,
AsyncArray,
Expand Down Expand Up @@ -289,7 +290,7 @@
async def open(
*,
store: StoreLike | None = None,
mode: AccessModeLiteral = "a",
mode: AccessModeLiteral | None = None,
zarr_version: ZarrFormat | None = None, # deprecated
zarr_format: ZarrFormat | None = None,
path: str | None = None,
Expand Down Expand Up @@ -324,7 +325,11 @@
Return type depends on what exists in the given store.
"""
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)

if mode is None:
if isinstance(store, Store) and store.read_only:
mode = "r"

Check warning on line 330 in src/zarr/api/asynchronous.py

View check run for this annotation

Codecov / codecov/patch

src/zarr/api/asynchronous.py#L328-L330

Added lines #L328 - L330 were not covered by tests
else:
mode = "a"

Check warning on line 332 in src/zarr/api/asynchronous.py

View check run for this annotation

Codecov / codecov/patch

src/zarr/api/asynchronous.py#L332

Added line #L332 was not covered by tests
store_path = await make_store_path(store, mode=mode, path=path, storage_options=storage_options)

# TODO: the mode check below seems wrong!
Expand Down
35 changes: 34 additions & 1 deletion tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
)
from zarr.core.buffer import NDArrayLike
from zarr.errors import MetadataValidationError
from zarr.storage import MemoryStore
from zarr.storage import LocalStore, MemoryStore, ZipStore
from zarr.storage._utils import normalize_path
from zarr.testing.utils import gpu_test

Expand Down Expand Up @@ -400,6 +400,39 @@ def test_load_array(memory_store: Store) -> None:
assert_array_equal(bar, array)


@pytest.mark.parametrize("path", ["data", None])
def test_load_zip(tmp_path: pathlib.Path, path: str | None) -> None:
file = tmp_path / "test.zip"
data = np.arange(100).reshape(10, 10)

with ZipStore(file, mode="w", read_only=False) as zs:
save(zs, data, path=path)
with ZipStore(file, mode="r", read_only=False) as zs:
result = zarr.load(store=zs, path=path)
assert np.array_equal(result, data)
with ZipStore(file, mode="r") as zs:
result = zarr.load(store=zs, path=path)
assert_array_equal(result, data)
with ZipStore(file, read_only=True) as zs:
result = zarr.load(store=zs, path=path)
assert_array_equal(result, data)


@pytest.mark.parametrize("path", ["data", None])
def test_load_local(tmp_path: pathlib.Path, path: str | None) -> None:
file = tmp_path / "test.zip"
data = np.arange(100).reshape(10, 10)

with LocalStore(file, read_only=False) as zs:
save(zs, data, path=path)
with LocalStore(file, read_only=False) as zs:
result = zarr.load(store=zs, path=path)
assert_array_equal(result, data)
with LocalStore(file, read_only=True) as zs:
result = zarr.load(store=zs, path=path)
assert_array_equal(result, data)


def test_tree() -> None:
pytest.importorskip("rich")
g1 = zarr.group()
Expand Down