diff --git a/.github/workflows/minimal.yml b/.github/workflows/minimal.yml index eb6ebd5d25..a87e374de7 100644 --- a/.github/workflows/minimal.yml +++ b/.github/workflows/minimal.yml @@ -22,12 +22,16 @@ jobs: activate-environment: minimal - name: Tests shell: "bash -l {0}" + env: + ZARR_V3_EXPERIMENTAL_API: 1 run: | conda activate minimal python -m pip install . pytest -svx --timeout=300 - name: Fixture generation shell: "bash -l {0}" + env: + ZARR_V3_EXPERIMENTAL_API: 1 run: | conda activate minimal rm -rf fixture/ diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 2cc1de1769..37000350cb 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -71,7 +71,7 @@ jobs: ZARR_TEST_ABS: 1 ZARR_TEST_MONGO: 1 ZARR_TEST_REDIS: 1 - ZARR_V3_API_AVAILABLE: 1 + ZARR_V3_EXPERIMENTAL_API: 1 run: | conda activate zarr-env mkdir ~/blob_emulator diff --git a/.github/workflows/windows-testing.yml b/.github/workflows/windows-testing.yml index 0bb5c1da70..8c249a4db4 100644 --- a/.github/workflows/windows-testing.yml +++ b/.github/workflows/windows-testing.yml @@ -51,6 +51,7 @@ jobs: pytest -sv --timeout=300 env: ZARR_TEST_ABS: 1 + ZARR_V3_EXPERIMENTAL_API: 1 - name: Conda info shell: bash -l {0} run: conda info diff --git a/docs/release.rst b/docs/release.rst index 0dbaacb8f7..09a0a4499c 100644 --- a/docs/release.rst +++ b/docs/release.rst @@ -23,7 +23,7 @@ Enhancements package has the necessary classes and functions for evaluating Zarr V3. Since the format is not yet finalized, the classes and functions are not automatically imported into the regular `zarr` name space. Setting the - `ZARR_V3_API_AVAILABLE` environment variable will activate them. + `ZARR_V3_EXPERIMENTAL_API` environment variable will activate them. By :user:`Greggory Lee `; :issue:`898`, :issue:`1006`, and :issue:`1007`. * **Create FSStore from an existing fsspec filesystem**. If you have created diff --git a/zarr/_storage/store.py b/zarr/_storage/store.py index 152c9abd6b..36a5c0bff5 100644 --- a/zarr/_storage/store.py +++ b/zarr/_storage/store.py @@ -18,7 +18,15 @@ DEFAULT_ZARR_VERSION = 2 -v3_api_available = os.environ.get('ZARR_V3_API_AVAILABLE', '0').lower() not in ['0', 'false'] +v3_api_available = os.environ.get('ZARR_V3_EXPERIMENTAL_API', '0').lower() not in ['0', 'false'] + + +def assert_zarr_v3_api_available(): + if not v3_api_available: + raise NotImplementedError( + "# V3 reading and writing is experimental! To enable support, set:\n" + "ZARR_V3_EXPERIMENTAL_API=1" + ) # pragma: no cover class BaseStore(MutableMapping): diff --git a/zarr/convenience.py b/zarr/convenience.py index 60e47dc339..93dc860477 100644 --- a/zarr/convenience.py +++ b/zarr/convenience.py @@ -5,7 +5,7 @@ import re from collections.abc import Mapping, MutableMapping -from zarr._storage.store import data_root, meta_root +from zarr._storage.store import data_root, meta_root, assert_zarr_v3_api_available from zarr.core import Array from zarr.creation import array as _create_array from zarr.creation import open_array @@ -1209,6 +1209,8 @@ def is_zarr_key(key): else: + assert_zarr_v3_api_available() + sfx = _get_metadata_suffix(store) # type: ignore def is_zarr_key(key): @@ -1288,6 +1290,7 @@ def open_consolidated(store: StoreLike, metadata_key=".zmetadata", mode="r+", ** if store._store_version == 2: ConsolidatedStoreClass = ConsolidatedMetadataStore else: + assert_zarr_v3_api_available() ConsolidatedStoreClass = ConsolidatedMetadataStoreV3 # default is to store within 'consolidated' group on v3 if not metadata_key.startswith('meta/root/'): diff --git a/zarr/core.py b/zarr/core.py index a60269d2e9..6ce2fa8800 100644 --- a/zarr/core.py +++ b/zarr/core.py @@ -11,7 +11,7 @@ import numpy as np from numcodecs.compat import ensure_bytes, ensure_ndarray -from zarr._storage.store import _prefix_to_attrs_key +from zarr._storage.store import _prefix_to_attrs_key, assert_zarr_v3_api_available from zarr.attrs import Attributes from zarr.codecs import AsType, get_codec from zarr.errors import ArrayNotFoundError, ReadOnlyError, ArrayIndexError @@ -171,6 +171,9 @@ def __init__( if zarr_version is None: zarr_version = store._store_version + if zarr_version != 2: + assert_zarr_v3_api_available() + if chunk_store is not None: chunk_store = normalize_store_arg(chunk_store, zarr_version=zarr_version) diff --git a/zarr/hierarchy.py b/zarr/hierarchy.py index d92af08ffb..b9052408b4 100644 --- a/zarr/hierarchy.py +++ b/zarr/hierarchy.py @@ -3,7 +3,8 @@ import numpy as np -from zarr._storage.store import _get_metadata_suffix, data_root, meta_root, DEFAULT_ZARR_VERSION +from zarr._storage.store import (_get_metadata_suffix, data_root, meta_root, + DEFAULT_ZARR_VERSION, assert_zarr_v3_api_available) from zarr.attrs import Attributes from zarr.core import Array 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, store: BaseStore = _normalize_store_arg(store, zarr_version=zarr_version) if zarr_version is None: zarr_version = getattr(store, '_store_version', DEFAULT_ZARR_VERSION) + + if zarr_version != 2: + assert_zarr_v3_api_available() + if chunk_store is not None: chunk_store: BaseStore = _normalize_store_arg(chunk_store, zarr_version=zarr_version) self._store = store @@ -1178,6 +1183,10 @@ def _normalize_store_arg(store, *, storage_options=None, mode="r", zarr_version=None): if zarr_version is None: zarr_version = getattr(store, '_store_version', DEFAULT_ZARR_VERSION) + + if zarr_version != 2: + assert_zarr_v3_api_available() + if store is None: return MemoryStore() if zarr_version == 2 else MemoryStoreV3() return normalize_store_arg(store, @@ -1234,6 +1243,10 @@ def group(store=None, overwrite=False, chunk_store=None, store = _normalize_store_arg(store, zarr_version=zarr_version) if zarr_version is None: zarr_version = getattr(store, '_store_version', DEFAULT_ZARR_VERSION) + + if zarr_version != 2: + assert_zarr_v3_api_available() + if zarr_version == 3 and path is None: raise ValueError(f"path must be provided for a v{zarr_version} group") path = normalize_storage_path(path) @@ -1305,6 +1318,10 @@ def open_group(store=None, mode='a', cache_attrs=True, synchronizer=None, path=N zarr_version=zarr_version) if zarr_version is None: zarr_version = getattr(store, '_store_version', DEFAULT_ZARR_VERSION) + + if zarr_version != 2: + assert_zarr_v3_api_available() + if chunk_store is not None: chunk_store = _normalize_store_arg(chunk_store, storage_options=storage_options, diff --git a/zarr/tests/test_convenience.py b/zarr/tests/test_convenience.py index ce8f03d0da..d0d293a694 100644 --- a/zarr/tests/test_convenience.py +++ b/zarr/tests/test_convenience.py @@ -34,6 +34,7 @@ meta_root, getsize, ) +from zarr._storage.store import v3_api_available from zarr._storage.v3 import ( ConsolidatedMetadataStoreV3, DirectoryStoreV3, @@ -44,6 +45,8 @@ ) from zarr.tests.util import have_fsspec +_VERSIONS = v3_api_available and (2, 3) or (2,) + def _init_creation_kwargs(zarr_version): kwargs = {'zarr_version': zarr_version} @@ -52,7 +55,7 @@ def _init_creation_kwargs(zarr_version): return kwargs -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_open_array(path_type, zarr_version): store = tempfile.mkdtemp() @@ -86,7 +89,7 @@ def test_open_array(path_type, zarr_version): open('doesnotexist', mode='r') -@pytest.mark.parametrize("zarr_version", [2, 3]) +@pytest.mark.parametrize("zarr_version", _VERSIONS) def test_open_group(path_type, zarr_version): store = tempfile.mkdtemp() @@ -116,7 +119,7 @@ def test_open_group(path_type, zarr_version): assert g.read_only -@pytest.mark.parametrize("zarr_version", [2, 3]) +@pytest.mark.parametrize("zarr_version", _VERSIONS) def test_save_errors(zarr_version): with pytest.raises(ValueError): # no arrays provided @@ -129,6 +132,7 @@ def test_save_errors(zarr_version): save('data/group.zarr', zarr_version=zarr_version) +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") def test_zarr_v3_save_multiple_unnamed(): x = np.ones(8) y = np.zeros(8) @@ -142,6 +146,7 @@ def test_zarr_v3_save_multiple_unnamed(): assert meta_root + 'dataset/arr_1.array.json' in store +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") def test_zarr_v3_save_errors(): x = np.ones(8) with pytest.raises(ValueError): @@ -155,7 +160,7 @@ def test_zarr_v3_save_errors(): save('data/group.zr3', x, zarr_version=3) -@pytest.mark.parametrize("zarr_version", [2, 3]) +@pytest.mark.parametrize("zarr_version", _VERSIONS) def test_lazy_loader(zarr_version): foo = np.arange(100) bar = np.arange(100, 0, -1) @@ -173,7 +178,7 @@ def test_lazy_loader(zarr_version): assert 'LazyLoader: ' in repr(loader) -@pytest.mark.parametrize("zarr_version", [2, 3]) +@pytest.mark.parametrize("zarr_version", _VERSIONS) def test_load_array(zarr_version): foo = np.arange(100) bar = np.arange(100, 0, -1) @@ -192,7 +197,7 @@ def test_load_array(zarr_version): assert_array_equal(bar, array) -@pytest.mark.parametrize("zarr_version", [2, 3]) +@pytest.mark.parametrize("zarr_version", _VERSIONS) def test_tree(zarr_version): kwargs = _init_creation_kwargs(zarr_version) g1 = zarr.group(**kwargs) @@ -205,7 +210,7 @@ def test_tree(zarr_version): assert str(zarr.tree(g1)) == str(g1.tree()) -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) @pytest.mark.parametrize('stores_from_path', [False, True]) @pytest.mark.parametrize( 'with_chunk_store,listable', @@ -531,6 +536,7 @@ def test_if_exists(self): copy_store(source, dest, if_exists='foobar') +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestCopyStoreV3(TestCopyStore): _version = 3 @@ -666,6 +672,7 @@ def test_copy_all(): assert destination_group.subgroup.attrs["info"] == "sub attrs" +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") def test_copy_all_v3(): """ https://github.com/zarr-developers/zarr-python/issues/269 @@ -931,6 +938,7 @@ def test_logging(self, source, dest, tmpdir): copy(source['foo'], dest, dry_run=True, log=True) +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestCopyV3(TestCopy): @pytest.fixture(params=['zarr', 'hdf5']) diff --git a/zarr/tests/test_core.py b/zarr/tests/test_core.py index 2212b035c2..f5f043e6e3 100644 --- a/zarr/tests/test_core.py +++ b/zarr/tests/test_core.py @@ -18,6 +18,7 @@ from pkg_resources import parse_version from zarr._storage.store import ( + v3_api_available, _prefix_to_array_key, _prefix_to_attrs_key, _prefix_to_group_key @@ -2704,7 +2705,7 @@ def test_read_from_all_blocks(self): # Start with TestArrayWithPathV3 not TestArrayV3 since path must be supplied - +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayV3(unittest.TestCase): version = 3 @@ -2734,6 +2735,7 @@ def test_prefix_exceptions(self): _prefix_to_attrs_key(store, '') +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithPathV3(TestArrayWithPath): version = 3 @@ -2868,6 +2870,7 @@ def test_nchunks_initialized(self): z.store.close() +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithChunkStoreV3(TestArrayWithChunkStore, TestArrayWithPathV3): @staticmethod @@ -2910,6 +2913,7 @@ def test_nbytes_stored(self): assert -1 == z.nbytes_stored +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithDirectoryStoreV3(TestArrayWithDirectoryStore, TestArrayWithPathV3): @staticmethod @@ -2937,6 +2941,7 @@ def test_nbytes_stored(self): @skip_test_env_var("ZARR_TEST_ABS") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithABSStoreV3(TestArrayWithABSStore, TestArrayWithPathV3): @staticmethod @@ -2961,6 +2966,7 @@ def create_array(self, array_path='arr1', read_only=False, **kwargs): # class TestArrayWithN5StoreV3(TestArrayWithDirectoryStoreV3): +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithDBMStoreV3(TestArrayWithDBMStore, TestArrayWithPathV3): @staticmethod @@ -2980,6 +2986,7 @@ def test_nbytes_stored(self): pass # not implemented +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithDBMStoreV3BerkeleyDB(TestArrayWithDBMStoreBerkeleyDB, TestArrayWithPathV3): @staticmethod @@ -3000,6 +3007,7 @@ def test_nbytes_stored(self): pass # not implemented +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithLMDBStoreV3(TestArrayWithLMDBStore, TestArrayWithPathV3): @staticmethod @@ -3023,6 +3031,7 @@ def test_nbytes_stored(self): pass # not implemented +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithLMDBStoreV3NoBuffers(TestArrayWithLMDBStoreNoBuffers, TestArrayWithPathV3): @staticmethod @@ -3043,6 +3052,7 @@ def test_nbytes_stored(self): pass # not implemented +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithSQLiteStoreV3(TestArrayWithPathV3, TestArrayWithSQLiteStore): @staticmethod @@ -3076,6 +3086,7 @@ def test_nbytes_stored(self): # custom store, does not support getsize() # Note: this custom mapping doesn't actually have all methods in the # v3 spec (e.g. erase), but they aren't needed here. + class CustomMappingV3(StoreV3): def __init__(self): @@ -3112,6 +3123,7 @@ def __contains__(self, item): return item in self.inner +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithCustomMappingV3(TestArrayWithPathV3, TestArrayWithCustomMapping): @staticmethod @@ -3140,6 +3152,7 @@ def test_len(self): assert len(z._store) == 2 +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayNoCacheV3(TestArrayWithPathV3, TestArrayNoCache): @staticmethod @@ -3158,6 +3171,7 @@ def test_object_arrays_danger(self): pass +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithStoreCacheV3(TestArrayWithPathV3, TestArrayWithStoreCache): @staticmethod @@ -3177,6 +3191,7 @@ def test_store_has_bytes_values(self): @pytest.mark.skipif(have_fsspec is False, reason="needs fsspec") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithFSStoreV3(TestArrayWithPathV3, TestArrayWithFSStore): @staticmethod def create_array(array_path='arr1', read_only=False, **kwargs): @@ -3208,6 +3223,7 @@ def expected(self): @pytest.mark.skipif(have_fsspec is False, reason="needs fsspec") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithFSStoreV3FromFilesystem(TestArrayWithPathV3, TestArrayWithFSStore): @staticmethod def create_array(array_path='arr1', read_only=False, **kwargs): @@ -3236,6 +3252,7 @@ def expected(self): @pytest.mark.skipif(have_fsspec is False, reason="needs fsspec") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithFSStoreV3PartialRead(TestArrayWithPathV3, TestArrayWithFSStorePartialRead): @staticmethod @@ -3269,6 +3286,7 @@ def expected(self): @pytest.mark.skipif(have_fsspec is False, reason="needs fsspec") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithFSStoreV3Nested(TestArrayWithPathV3, TestArrayWithFSStoreNested): @staticmethod @@ -3296,6 +3314,7 @@ def expected(self): @pytest.mark.skipif(have_fsspec is False, reason="needs fsspec") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestArrayWithFSStoreV3NestedPartialRead(TestArrayWithPathV3, TestArrayWithFSStoreNestedPartialRead): @staticmethod @@ -3329,6 +3348,7 @@ def expected(self): ] +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") def test_array_mismatched_store_versions(): store_v3 = KVStoreV3(dict()) store_v2 = KVStore(dict()) diff --git a/zarr/tests/test_creation.py b/zarr/tests/test_creation.py index 863b987d9b..48d6aee4f5 100644 --- a/zarr/tests/test_creation.py +++ b/zarr/tests/test_creation.py @@ -17,9 +17,13 @@ from zarr.hierarchy import open_group from zarr.n5 import N5Store from zarr.storage import DirectoryStore, KVStore +from zarr._storage.store import v3_api_available from zarr._storage.v3 import DirectoryStoreV3, KVStoreV3 from zarr.sync import ThreadSynchronizer +_VERSIONS = v3_api_available and (None, 2, 3) or (None, 2) +_VERSIONS2 = v3_api_available and (2, 3) or (2,) + # something bcolz-like class MockBcolzArray: @@ -56,7 +60,7 @@ def _init_creation_kwargs(zarr_version): return kwargs -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_array(zarr_version): expected_zarr_version = DEFAULT_ZARR_VERSION if zarr_version is None else zarr_version @@ -116,7 +120,7 @@ def test_array(zarr_version): assert np.dtype('i8') == z.dtype -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_empty(zarr_version): kwargs = _init_creation_kwargs(zarr_version) z = empty(100, chunks=10, **kwargs) @@ -124,7 +128,7 @@ def test_empty(zarr_version): assert (10,) == z.chunks -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_zeros(zarr_version): kwargs = _init_creation_kwargs(zarr_version) z = zeros(100, chunks=10, **kwargs) @@ -133,7 +137,7 @@ def test_zeros(zarr_version): assert_array_equal(np.zeros(100), z[:]) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_ones(zarr_version): kwargs = _init_creation_kwargs(zarr_version) z = ones(100, chunks=10, **kwargs) @@ -142,7 +146,7 @@ def test_ones(zarr_version): assert_array_equal(np.ones(100), z[:]) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_full(zarr_version): kwargs = _init_creation_kwargs(zarr_version) z = full(100, chunks=10, fill_value=42, dtype='i4', **kwargs) @@ -155,7 +159,7 @@ def test_full(zarr_version): assert np.all(np.isnan(z[:])) -@pytest.mark.parametrize('zarr_version', [None, 2]) +@pytest.mark.parametrize('zarr_version', [None, 2]) # TODO def test_full_additional_dtypes(zarr_version): """Test additional types that aren't part of the base v3 spec.""" kwargs = _init_creation_kwargs(zarr_version) @@ -190,7 +194,7 @@ def test_full_additional_dtypes(zarr_version): @pytest.mark.parametrize('dimension_separator', ['.', '/', None]) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_open_array(zarr_version, dimension_separator): store = 'data/array.zarr' @@ -317,7 +321,7 @@ def test_open_array_none(): @pytest.mark.parametrize('dimension_separator', ['.', '/', None]) -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS2) def test_open_array_infer_separator_from_store(zarr_version, dimension_separator): if zarr_version == 3: @@ -386,7 +390,7 @@ def test_open_array_n5(zarr_version): assert_array_equal(np.full(100, fill_value=42), a[:]) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_open_array_dict_store(zarr_version): # dict will become a KVStore @@ -404,7 +408,7 @@ def test_open_array_dict_store(zarr_version): assert_array_equal(np.full(100, fill_value=42), z[:]) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_create_in_dict(zarr_version): kwargs = _init_creation_kwargs(zarr_version) expected_store_type = KVStoreV3 if zarr_version == 3 else KVStore @@ -417,7 +421,7 @@ def test_create_in_dict(zarr_version): assert isinstance(a.store, expected_store_type) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_empty_like(zarr_version): kwargs = _init_creation_kwargs(zarr_version) expected_zarr_version = DEFAULT_ZARR_VERSION if zarr_version is None else zarr_version @@ -466,7 +470,7 @@ def test_empty_like(zarr_version): assert isinstance(z.chunks, tuple) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_zeros_like(zarr_version): kwargs = _init_creation_kwargs(zarr_version) @@ -493,7 +497,7 @@ def test_zeros_like(zarr_version): assert 0 == z3.fill_value -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_ones_like(zarr_version): kwargs = _init_creation_kwargs(zarr_version) @@ -521,7 +525,7 @@ def test_ones_like(zarr_version): assert z3._store._store_version == expected_zarr_version -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_full_like(zarr_version): kwargs = _init_creation_kwargs(zarr_version) @@ -551,7 +555,7 @@ def test_full_like(zarr_version): full_like(a, chunks=10, **kwargs) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_open_like(zarr_version): kwargs = _init_creation_kwargs(zarr_version) expected_zarr_version = DEFAULT_ZARR_VERSION if zarr_version is None else zarr_version @@ -582,7 +586,7 @@ def test_open_like(zarr_version): assert z3._store._store_version == expected_zarr_version -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_create(zarr_version): kwargs = _init_creation_kwargs(zarr_version) expected_zarr_version = DEFAULT_ZARR_VERSION if zarr_version is None else zarr_version @@ -654,7 +658,7 @@ def test_create(zarr_version): assert z.chunks == z.shape -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_compression_args(zarr_version): kwargs = _init_creation_kwargs(zarr_version) @@ -689,7 +693,7 @@ def test_compression_args(zarr_version): create(100, compressor=Zlib(9), compression_opts=1, **kwargs) -@pytest.mark.parametrize('zarr_version', [None, 2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_create_read_only(zarr_version): # https://github.com/alimanfoo/zarr/issues/151 diff --git a/zarr/tests/test_hierarchy.py b/zarr/tests/test_hierarchy.py index 3bcd826882..7c2eaa3f75 100644 --- a/zarr/tests/test_hierarchy.py +++ b/zarr/tests/test_hierarchy.py @@ -18,7 +18,7 @@ from numcodecs import Zlib from numpy.testing import assert_array_equal -from zarr._storage.store import _get_metadata_suffix +from zarr._storage.store import _get_metadata_suffix, v3_api_available from zarr.attrs import Attributes from zarr.core import Array from zarr.creation import open_array @@ -35,7 +35,11 @@ from zarr.tests.util import skip_test_env_var, have_fsspec, abs_container +_VERSIONS = v3_api_available and (2, 3) or (2,) + # noinspection PyStatementEffect + + class TestGroup(unittest.TestCase): @staticmethod @@ -1095,6 +1099,7 @@ def test_group_init_from_dict(chunk_dict): # noinspection PyStatementEffect +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3(TestGroup, unittest.TestCase): @staticmethod @@ -1153,6 +1158,7 @@ def create_store(): # noinspection PyStatementEffect +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithMemoryStore(TestGroupWithMemoryStore, TestGroupV3): @staticmethod @@ -1170,6 +1176,7 @@ def create_store(): return store, None +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithDirectoryStore(TestGroupWithDirectoryStore, TestGroupV3): @staticmethod @@ -1197,6 +1204,7 @@ def test_pickle(self): @skip_test_env_var("ZARR_TEST_ABS") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithABSStore(TestGroupV3): @staticmethod @@ -1246,6 +1254,7 @@ def test_round_trip_nd(self): @pytest.mark.skipif(have_fsspec is False, reason="needs fsspec") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithFSStore(TestGroupWithFSStore, TestGroupV3): @staticmethod @@ -1303,6 +1312,7 @@ def test_inconsistent_dimension_separator(self): @pytest.mark.skipif(have_fsspec is False, reason="needs fsspec") +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithNestedFSStore(TestGroupV3WithFSStore): @staticmethod @@ -1352,6 +1362,7 @@ def test_move(self): pass +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithZipStore(TestGroupWithZipStore, TestGroupV3): @staticmethod @@ -1372,6 +1383,7 @@ def create_store(): return store, None +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithDBMStore(TestGroupWithDBMStore, TestGroupV3): @staticmethod @@ -1393,6 +1405,7 @@ def create_store(): return store, None +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithDBMStoreBerkeleyDB(TestGroupWithDBMStoreBerkeleyDB, TestGroupV3): @staticmethod @@ -1415,6 +1428,7 @@ def create_store(): return store, None +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithLMDBStore(TestGroupWithLMDBStore, TestGroupV3): @staticmethod @@ -1436,6 +1450,7 @@ def create_store(self): return store, None +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithSQLiteStore(TestGroupWithSQLiteStore, TestGroupV3): def create_store(self): @@ -1477,6 +1492,7 @@ def test_chunk_store(self): assert expect == actual +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithChunkStore(TestGroupWithChunkStore, TestGroupV3): @staticmethod @@ -1520,6 +1536,7 @@ def create_store(): return store, None +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") class TestGroupV3WithStoreCache(TestGroupWithStoreCache, TestGroupV3): @staticmethod @@ -1528,7 +1545,7 @@ def create_store(): return store, None -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_group(zarr_version): # test the group() convenience function @@ -1572,7 +1589,7 @@ def test_group(zarr_version): assert store is g.store -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_open_group(zarr_version): # test the open_group() convenience function @@ -1639,7 +1656,7 @@ def test_open_group(zarr_version): assert 'foo/bar' == g.path -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_group_completions(zarr_version): path = None if zarr_version == 2 else 'group1' g = group(path=path, zarr_version=zarr_version) @@ -1670,7 +1687,7 @@ def test_group_completions(zarr_version): assert '456' not in d # not valid identifier -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_group_key_completions(zarr_version): path = None if zarr_version == 2 else 'group1' g = group(path=path, zarr_version=zarr_version) @@ -1754,7 +1771,7 @@ def _check_tree(g, expect_bytes, expect_text): isinstance(widget, ipytree.Tree) -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_tree(zarr_version): # setup path = None if zarr_version == 2 else 'group1' @@ -1821,6 +1838,7 @@ def test_tree(zarr_version): _check_tree(g3, expect_bytes, expect_text) +@pytest.mark.skipif(not v3_api_available, reason="V3 is disabled") def test_group_mismatched_store_versions(): store_v3 = KVStoreV3(dict()) store_v2 = KVStore(dict()) @@ -1854,7 +1872,7 @@ def test_group_mismatched_store_versions(): Group(store_v3, path='group2', read_only=True, chunk_store=chunk_store_v3) -@pytest.mark.parametrize('zarr_version', [2, 3]) +@pytest.mark.parametrize('zarr_version', _VERSIONS) def test_open_group_from_paths(zarr_version): """Verify zarr_version is applied to both the store and chunk_store.""" store = tempfile.mkdtemp() diff --git a/zarr/tests/test_storage_v3.py b/zarr/tests/test_storage_v3.py index a5011cf696..a33f274621 100644 --- a/zarr/tests/test_storage_v3.py +++ b/zarr/tests/test_storage_v3.py @@ -41,6 +41,12 @@ skip_if_nested_chunks) +pytestmark = pytest.mark.skipif( + not v3_api_available, + reason="v3 api is not available" +) + + @pytest.fixture(params=[ (None, "/"), (".", "."),