Skip to content

Commit d5a081c

Browse files
authored
Mark V3 API experimental (#1032)
* Mark V3 API experimental Following discussion at this week's community call, a few additional precautions are being implemented here: - [x] rename the var ZARR_V3_API_AVAILABLE to ZARR_V3_EXPERIMENTAL_API - [x] add an assertion to prevent use of zarr_version=3 w/o the var * Activate ZVEA for minimal.yml * First round of skipping v3 tests w/o env * Activate ZVEA for windows.yml * Skip all v3 tests if api is not enabled * skip codecov for the assertion
1 parent bd7ab16 commit d5a081c

File tree

13 files changed

+131
-39
lines changed

13 files changed

+131
-39
lines changed

.github/workflows/minimal.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ jobs:
2222
activate-environment: minimal
2323
- name: Tests
2424
shell: "bash -l {0}"
25+
env:
26+
ZARR_V3_EXPERIMENTAL_API: 1
2527
run: |
2628
conda activate minimal
2729
python -m pip install .
2830
pytest -svx --timeout=300
2931
- name: Fixture generation
3032
shell: "bash -l {0}"
33+
env:
34+
ZARR_V3_EXPERIMENTAL_API: 1
3135
run: |
3236
conda activate minimal
3337
rm -rf fixture/

.github/workflows/python-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
ZARR_TEST_ABS: 1
7272
ZARR_TEST_MONGO: 1
7373
ZARR_TEST_REDIS: 1
74-
ZARR_V3_API_AVAILABLE: 1
74+
ZARR_V3_EXPERIMENTAL_API: 1
7575
run: |
7676
conda activate zarr-env
7777
mkdir ~/blob_emulator

.github/workflows/windows-testing.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
pytest -sv --timeout=300
5252
env:
5353
ZARR_TEST_ABS: 1
54+
ZARR_V3_EXPERIMENTAL_API: 1
5455
- name: Conda info
5556
shell: bash -l {0}
5657
run: conda info

docs/release.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Enhancements
2323
package has the necessary classes and functions for evaluating Zarr V3.
2424
Since the format is not yet finalized, the classes and functions are not
2525
automatically imported into the regular `zarr` name space. Setting the
26-
`ZARR_V3_API_AVAILABLE` environment variable will activate them.
26+
`ZARR_V3_EXPERIMENTAL_API` environment variable will activate them.
2727
By :user:`Greggory Lee <grlee77>`; :issue:`898`, :issue:`1006`, and :issue:`1007`.
2828

2929
* **Create FSStore from an existing fsspec filesystem**. If you have created

zarr/_storage/store.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@
1818

1919
DEFAULT_ZARR_VERSION = 2
2020

21-
v3_api_available = os.environ.get('ZARR_V3_API_AVAILABLE', '0').lower() not in ['0', 'false']
21+
v3_api_available = os.environ.get('ZARR_V3_EXPERIMENTAL_API', '0').lower() not in ['0', 'false']
22+
23+
24+
def assert_zarr_v3_api_available():
25+
if not v3_api_available:
26+
raise NotImplementedError(
27+
"# V3 reading and writing is experimental! To enable support, set:\n"
28+
"ZARR_V3_EXPERIMENTAL_API=1"
29+
) # pragma: no cover
2230

2331

2432
class BaseStore(MutableMapping):

zarr/convenience.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
from collections.abc import Mapping, MutableMapping
77

8-
from zarr._storage.store import data_root, meta_root
8+
from zarr._storage.store import data_root, meta_root, assert_zarr_v3_api_available
99
from zarr.core import Array
1010
from zarr.creation import array as _create_array
1111
from zarr.creation import open_array
@@ -1209,6 +1209,8 @@ def is_zarr_key(key):
12091209

12101210
else:
12111211

1212+
assert_zarr_v3_api_available()
1213+
12121214
sfx = _get_metadata_suffix(store) # type: ignore
12131215

12141216
def is_zarr_key(key):
@@ -1288,6 +1290,7 @@ def open_consolidated(store: StoreLike, metadata_key=".zmetadata", mode="r+", **
12881290
if store._store_version == 2:
12891291
ConsolidatedStoreClass = ConsolidatedMetadataStore
12901292
else:
1293+
assert_zarr_v3_api_available()
12911294
ConsolidatedStoreClass = ConsolidatedMetadataStoreV3
12921295
# default is to store within 'consolidated' group on v3
12931296
if not metadata_key.startswith('meta/root/'):

zarr/core.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import numpy as np
1212
from numcodecs.compat import ensure_bytes, ensure_ndarray
1313

14-
from zarr._storage.store import _prefix_to_attrs_key
14+
from zarr._storage.store import _prefix_to_attrs_key, assert_zarr_v3_api_available
1515
from zarr.attrs import Attributes
1616
from zarr.codecs import AsType, get_codec
1717
from zarr.errors import ArrayNotFoundError, ReadOnlyError, ArrayIndexError
@@ -171,6 +171,9 @@ def __init__(
171171
if zarr_version is None:
172172
zarr_version = store._store_version
173173

174+
if zarr_version != 2:
175+
assert_zarr_v3_api_available()
176+
174177
if chunk_store is not None:
175178
chunk_store = normalize_store_arg(chunk_store,
176179
zarr_version=zarr_version)

zarr/hierarchy.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
import numpy as np
55

6-
from zarr._storage.store import _get_metadata_suffix, data_root, meta_root, DEFAULT_ZARR_VERSION
6+
from zarr._storage.store import (_get_metadata_suffix, data_root, meta_root,
7+
DEFAULT_ZARR_VERSION, assert_zarr_v3_api_available)
78
from zarr.attrs import Attributes
89
from zarr.core import Array
910
from zarr.creation import (array, create, empty, empty_like, full, full_like,
@@ -117,6 +118,10 @@ def __init__(self, store, path=None, read_only=False, chunk_store=None,
117118
store: BaseStore = _normalize_store_arg(store, zarr_version=zarr_version)
118119
if zarr_version is None:
119120
zarr_version = getattr(store, '_store_version', DEFAULT_ZARR_VERSION)
121+
122+
if zarr_version != 2:
123+
assert_zarr_v3_api_available()
124+
120125
if chunk_store is not None:
121126
chunk_store: BaseStore = _normalize_store_arg(chunk_store, zarr_version=zarr_version)
122127
self._store = store
@@ -1178,6 +1183,10 @@ def _normalize_store_arg(store, *, storage_options=None, mode="r",
11781183
zarr_version=None):
11791184
if zarr_version is None:
11801185
zarr_version = getattr(store, '_store_version', DEFAULT_ZARR_VERSION)
1186+
1187+
if zarr_version != 2:
1188+
assert_zarr_v3_api_available()
1189+
11811190
if store is None:
11821191
return MemoryStore() if zarr_version == 2 else MemoryStoreV3()
11831192
return normalize_store_arg(store,
@@ -1234,6 +1243,10 @@ def group(store=None, overwrite=False, chunk_store=None,
12341243
store = _normalize_store_arg(store, zarr_version=zarr_version)
12351244
if zarr_version is None:
12361245
zarr_version = getattr(store, '_store_version', DEFAULT_ZARR_VERSION)
1246+
1247+
if zarr_version != 2:
1248+
assert_zarr_v3_api_available()
1249+
12371250
if zarr_version == 3 and path is None:
12381251
raise ValueError(f"path must be provided for a v{zarr_version} group")
12391252
path = normalize_storage_path(path)
@@ -1305,6 +1318,10 @@ def open_group(store=None, mode='a', cache_attrs=True, synchronizer=None, path=N
13051318
zarr_version=zarr_version)
13061319
if zarr_version is None:
13071320
zarr_version = getattr(store, '_store_version', DEFAULT_ZARR_VERSION)
1321+
1322+
if zarr_version != 2:
1323+
assert_zarr_v3_api_available()
1324+
13081325
if chunk_store is not None:
13091326
chunk_store = _normalize_store_arg(chunk_store,
13101327
storage_options=storage_options,

zarr/tests/test_convenience.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
meta_root,
3535
getsize,
3636
)
37+
from zarr._storage.store import v3_api_available
3738
from zarr._storage.v3 import (
3839
ConsolidatedMetadataStoreV3,
3940
DirectoryStoreV3,
@@ -44,6 +45,8 @@
4445
)
4546
from zarr.tests.util import have_fsspec
4647

48+
_VERSIONS = v3_api_available and (2, 3) or (2,)
49+
4750

4851
def _init_creation_kwargs(zarr_version):
4952
kwargs = {'zarr_version': zarr_version}
@@ -52,7 +55,7 @@ def _init_creation_kwargs(zarr_version):
5255
return kwargs
5356

5457

55-
@pytest.mark.parametrize('zarr_version', [2, 3])
58+
@pytest.mark.parametrize('zarr_version', _VERSIONS)
5659
def test_open_array(path_type, zarr_version):
5760

5861
store = tempfile.mkdtemp()
@@ -86,7 +89,7 @@ def test_open_array(path_type, zarr_version):
8689
open('doesnotexist', mode='r')
8790

8891

89-
@pytest.mark.parametrize("zarr_version", [2, 3])
92+
@pytest.mark.parametrize("zarr_version", _VERSIONS)
9093
def test_open_group(path_type, zarr_version):
9194

9295
store = tempfile.mkdtemp()
@@ -116,7 +119,7 @@ def test_open_group(path_type, zarr_version):
116119
assert g.read_only
117120

118121

119-
@pytest.mark.parametrize("zarr_version", [2, 3])
122+
@pytest.mark.parametrize("zarr_version", _VERSIONS)
120123
def test_save_errors(zarr_version):
121124
with pytest.raises(ValueError):
122125
# no arrays provided
@@ -129,6 +132,7 @@ def test_save_errors(zarr_version):
129132
save('data/group.zarr', zarr_version=zarr_version)
130133

131134

135+
@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled")
132136
def test_zarr_v3_save_multiple_unnamed():
133137
x = np.ones(8)
134138
y = np.zeros(8)
@@ -142,6 +146,7 @@ def test_zarr_v3_save_multiple_unnamed():
142146
assert meta_root + 'dataset/arr_1.array.json' in store
143147

144148

149+
@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled")
145150
def test_zarr_v3_save_errors():
146151
x = np.ones(8)
147152
with pytest.raises(ValueError):
@@ -155,7 +160,7 @@ def test_zarr_v3_save_errors():
155160
save('data/group.zr3', x, zarr_version=3)
156161

157162

158-
@pytest.mark.parametrize("zarr_version", [2, 3])
163+
@pytest.mark.parametrize("zarr_version", _VERSIONS)
159164
def test_lazy_loader(zarr_version):
160165
foo = np.arange(100)
161166
bar = np.arange(100, 0, -1)
@@ -173,7 +178,7 @@ def test_lazy_loader(zarr_version):
173178
assert 'LazyLoader: ' in repr(loader)
174179

175180

176-
@pytest.mark.parametrize("zarr_version", [2, 3])
181+
@pytest.mark.parametrize("zarr_version", _VERSIONS)
177182
def test_load_array(zarr_version):
178183
foo = np.arange(100)
179184
bar = np.arange(100, 0, -1)
@@ -192,7 +197,7 @@ def test_load_array(zarr_version):
192197
assert_array_equal(bar, array)
193198

194199

195-
@pytest.mark.parametrize("zarr_version", [2, 3])
200+
@pytest.mark.parametrize("zarr_version", _VERSIONS)
196201
def test_tree(zarr_version):
197202
kwargs = _init_creation_kwargs(zarr_version)
198203
g1 = zarr.group(**kwargs)
@@ -205,7 +210,7 @@ def test_tree(zarr_version):
205210
assert str(zarr.tree(g1)) == str(g1.tree())
206211

207212

208-
@pytest.mark.parametrize('zarr_version', [2, 3])
213+
@pytest.mark.parametrize('zarr_version', _VERSIONS)
209214
@pytest.mark.parametrize('stores_from_path', [False, True])
210215
@pytest.mark.parametrize(
211216
'with_chunk_store,listable',
@@ -531,6 +536,7 @@ def test_if_exists(self):
531536
copy_store(source, dest, if_exists='foobar')
532537

533538

539+
@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled")
534540
class TestCopyStoreV3(TestCopyStore):
535541

536542
_version = 3
@@ -666,6 +672,7 @@ def test_copy_all():
666672
assert destination_group.subgroup.attrs["info"] == "sub attrs"
667673

668674

675+
@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled")
669676
def test_copy_all_v3():
670677
"""
671678
https://github.com/zarr-developers/zarr-python/issues/269
@@ -931,6 +938,7 @@ def test_logging(self, source, dest, tmpdir):
931938
copy(source['foo'], dest, dry_run=True, log=True)
932939

933940

941+
@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled")
934942
class TestCopyV3(TestCopy):
935943

936944
@pytest.fixture(params=['zarr', 'hdf5'])

0 commit comments

Comments
 (0)