Skip to content

Commit b1ecdd5

Browse files
authored
fix: opening a group with unspecified format finds either v2 or v3 (#2183)
metadata objects
1 parent ceb3b36 commit b1ecdd5

File tree

4 files changed

+48
-27
lines changed

4 files changed

+48
-27
lines changed

src/zarr/api/asynchronous.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from zarr.core.array import Array, AsyncArray
1111
from zarr.core.common import JSON, AccessModeLiteral, ChunkCoords, MemoryOrder, ZarrFormat
12+
from zarr.core.config import config
1213
from zarr.core.group import AsyncGroup
1314
from zarr.core.metadata.v2 import ArrayV2Metadata
1415
from zarr.core.metadata.v3 import ArrayV3Metadata
@@ -126,8 +127,7 @@ def _handle_zarr_version_or_format(
126127

127128
def _default_zarr_version() -> ZarrFormat:
128129
"""return the default zarr_version"""
129-
# TODO: set default value from config
130-
return 3
130+
return cast(ZarrFormat, int(config.get("default_zarr_version", 3)))
131131

132132

133133
async def consolidate_metadata(*args: Any, **kwargs: Any) -> AsyncGroup:
@@ -337,7 +337,10 @@ async def save_group(
337337
kwargs
338338
NumPy arrays with data to save.
339339
"""
340-
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
340+
zarr_format = (
341+
_handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
342+
or _default_zarr_version()
343+
)
341344

342345
if len(args) == 0 and len(kwargs) == 0:
343346
raise ValueError("at least one array must be provided")
@@ -448,10 +451,7 @@ async def group(
448451
The new group.
449452
"""
450453

451-
zarr_format = (
452-
_handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
453-
or _default_zarr_version()
454-
)
454+
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
455455

456456
store_path = await make_store_path(store)
457457
if path is not None:
@@ -474,7 +474,7 @@ async def group(
474474
except (KeyError, FileNotFoundError):
475475
return await AsyncGroup.create(
476476
store=store_path,
477-
zarr_format=zarr_format,
477+
zarr_format=zarr_format or _default_zarr_version(),
478478
exists_ok=overwrite,
479479
attributes=attributes,
480480
)
@@ -483,7 +483,7 @@ async def group(
483483
async def open_group(
484484
*, # Note: this is a change from v2
485485
store: StoreLike | None = None,
486-
mode: AccessModeLiteral | None = None, # not used
486+
mode: AccessModeLiteral | None = None,
487487
cache_attrs: bool | None = None, # not used, default changed
488488
synchronizer: Any = None, # not used
489489
path: str | None = None,
@@ -538,10 +538,7 @@ async def open_group(
538538
The new group.
539539
"""
540540

541-
zarr_format = (
542-
_handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
543-
or _default_zarr_version()
544-
)
541+
zarr_format = _handle_zarr_version_or_format(zarr_version=zarr_version, zarr_format=zarr_format)
545542

546543
if cache_attrs is not None:
547544
warnings.warn("cache_attrs is not yet implemented", RuntimeWarning, stacklevel=2)
@@ -565,7 +562,10 @@ async def open_group(
565562
return await AsyncGroup.open(store_path, zarr_format=zarr_format)
566563
except (KeyError, FileNotFoundError):
567564
return await AsyncGroup.create(
568-
store_path, zarr_format=zarr_format, exists_ok=True, attributes=attributes
565+
store_path,
566+
zarr_format=zarr_format or _default_zarr_version(),
567+
exists_ok=True,
568+
attributes=attributes,
569569
)
570570

571571

@@ -687,7 +687,7 @@ async def create(
687687

688688
if zarr_format == 2 and chunks is None:
689689
chunks = shape
690-
if zarr_format == 3 and chunk_shape is None:
690+
elif zarr_format == 3 and chunk_shape is None:
691691
if chunks is not None:
692692
chunk_shape = chunks
693693
chunks = None
@@ -908,7 +908,7 @@ async def open_array(
908908
if store_path.store.mode.create:
909909
return await create(
910910
store=store_path,
911-
zarr_format=zarr_format,
911+
zarr_format=zarr_format or _default_zarr_version(),
912912
overwrite=store_path.store.mode.overwrite,
913913
**kwargs,
914914
)

src/zarr/core/config.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,20 @@ def reset(self) -> None:
2828
self.refresh()
2929

3030

31-
"""
32-
The config module is responsible for managing the configuration of zarr and is based on the Donfig python library.
33-
For selecting custom implementations of codecs, pipelines, buffers and ndbuffers, first register the implementations
34-
in the registry and then select them in the config.
35-
e.g. an implementation of the bytes codec in a class "NewBytesCodec", requires the value of codecs.bytes.name to be
36-
"NewBytesCodec".
37-
Donfig can be configured programmatically, by environment variables, or from YAML files in standard locations
38-
e.g. export ZARR_CODECS__BYTES__NAME="NewBytesCodec"
39-
(for more information see github.com/pytroll/donfig)
40-
Default values below point to the standard implementations of zarr-python
41-
"""
31+
# The config module is responsible for managing the configuration of zarr and is based on the Donfig python library.
32+
# For selecting custom implementations of codecs, pipelines, buffers and ndbuffers, first register the implementations
33+
# in the registry and then select them in the config.
34+
# e.g. an implementation of the bytes codec in a class "NewBytesCodec", requires the value of codecs.bytes.name to be
35+
# "NewBytesCodec".
36+
# Donfig can be configured programmatically, by environment variables, or from YAML files in standard locations
37+
# e.g. export ZARR_CODECS__BYTES__NAME="NewBytesCodec"
38+
# (for more information see github.com/pytroll/donfig)
39+
# Default values below point to the standard implementations of zarr-python
4240
config = Config(
4341
"zarr",
4442
defaults=[
4543
{
44+
"default_zarr_version": 3,
4645
"array": {"order": "C"},
4746
"async": {"concurrency": None, "timeout": None},
4847
"json_indent": 2,

tests/v3/test_api.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from zarr import Array, Group
99
from zarr.abc.store import Store
1010
from zarr.api.synchronous import create, load, open, open_group, save, save_array, save_group
11+
from zarr.core.common import ZarrFormat
1112
from zarr.store.memory import MemoryStore
1213

1314

@@ -81,6 +82,26 @@ async def test_open_group(memory_store: MemoryStore) -> None:
8182
# assert g.read_only
8283

8384

85+
@pytest.mark.parametrize("zarr_format", [None, 2, 3])
86+
async def test_open_group_unspecified_version(
87+
tmpdir: pathlib.Path, zarr_format: ZarrFormat
88+
) -> None:
89+
"""regression test for https://github.com/zarr-developers/zarr-python/issues/2175"""
90+
91+
# create a group with specified zarr format (could be 2, 3, or None)
92+
_ = await zarr.api.asynchronous.open_group(
93+
store=str(tmpdir), mode="w", zarr_format=zarr_format, attributes={"foo": "bar"}
94+
)
95+
96+
# now open that group without specifying the format
97+
g2 = await zarr.api.asynchronous.open_group(store=str(tmpdir), mode="r")
98+
99+
assert g2.attrs == {"foo": "bar"}
100+
101+
if zarr_format is not None:
102+
assert g2.metadata.zarr_format == zarr_format
103+
104+
84105
def test_save_errors() -> None:
85106
with pytest.raises(ValueError):
86107
# no arrays provided

tests/v3/test_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def test_config_defaults_set() -> None:
3939
# regression test for available defaults
4040
assert config.defaults == [
4141
{
42+
"default_zarr_version": 3,
4243
"array": {"order": "C"},
4344
"async": {"concurrency": None, "timeout": None},
4445
"json_indent": 2,

0 commit comments

Comments
 (0)