Skip to content

Commit cedcc27

Browse files
authored
Make obstore the default for cloud stores, if it is installed (#914)
Add test for use_obstore
1 parent 093092f commit cedcc27

3 files changed

Lines changed: 46 additions & 10 deletions

File tree

cubed/storage/stores/zarr_python_v3.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
import zarr
55

66
from cubed.types import T_DType, T_RegularChunks, T_Shape, T_Store
7+
from cubed.utils import is_cloud_storage_path
8+
9+
try:
10+
import obstore
11+
except ImportError:
12+
obstore = None # type: ignore
713

814
zarr.config.set(
915
{
@@ -46,21 +52,35 @@ def open_zarr_v3_array(
4652
path: Optional[str] = None,
4753
**kwargs,
4854
):
49-
# use obstore if requested
55+
# use obstore if explicitly requested, or if library is installed and store is a cloud store
5056
storage_options = kwargs.pop("storage_options", None)
51-
if storage_options is not None and storage_options.get("use_obstore", False):
52-
import obstore as obs
57+
obstore_requested = storage_options is not None and storage_options.get(
58+
"use_obstore", False
59+
)
60+
obstore_installed = obstore is not None
61+
if obstore_requested and not obstore_installed:
62+
raise RuntimeError(
63+
"obstore was requested with 'use_obstore=True' but it is not installed"
64+
)
65+
use_obstore = obstore_requested or (
66+
obstore_installed
67+
and isinstance(store, (str, Path))
68+
and is_cloud_storage_path(store)
69+
)
70+
if use_obstore:
5371
from zarr.storage import ObjectStore
5472

5573
if isinstance(store, str):
5674
if "://" not in store:
5775
p = Path(store)
58-
store = ObjectStore(obs.store.from_url(p.as_uri(), mkdir=True))
76+
store = ObjectStore(obstore.store.from_url(p.as_uri(), mkdir=True))
5977
else:
60-
store = ObjectStore(obs.store.from_url(store))
78+
store = ObjectStore(obstore.store.from_url(store))
6179
elif isinstance(store, Path):
6280
p = store
63-
store = ObjectStore(obs.store.from_url(p.as_uri(), mkdir=True))
81+
store = ObjectStore(obstore.store.from_url(p.as_uri(), mkdir=True))
82+
else:
83+
raise RuntimeError("Store must be a string or `Path` object for obstore")
6484

6585
if isinstance(chunks, int):
6686
chunks = (chunks,)

cubed/tests/storage/test_zarr.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,36 @@
11
import pytest
22
from zarr.codecs import BloscCodec, BytesCodec, ZstdCodec
3+
from zarr.storage import LocalStore, ObjectStore
34

45
from cubed import config
56
from cubed.storage.store import open_storage_array
67
from cubed.storage.zarr import lazy_zarr_array
78

89

9-
def test_lazy_zarr_array(tmp_path):
10+
@pytest.mark.parametrize("use_obstore", [None, False, True])
11+
def test_lazy_zarr_array(tmp_path, use_obstore):
1012
zarr_path = tmp_path / "lazy.zarr"
11-
arr = lazy_zarr_array(zarr_path, shape=(3, 3), dtype=int, chunks=(2, 2))
13+
if use_obstore is None:
14+
kwargs = {}
15+
else:
16+
kwargs = dict(storage_options=dict(use_obstore=use_obstore))
17+
arr = lazy_zarr_array(
18+
zarr_path,
19+
shape=(3, 3),
20+
dtype=int,
21+
chunks=(2, 2),
22+
**kwargs,
23+
)
1224

1325
assert not zarr_path.exists()
1426
with pytest.raises((FileNotFoundError, TypeError, ValueError)):
1527
arr.open()
1628

17-
arr.create()
29+
za = arr.create()
30+
if use_obstore:
31+
assert isinstance(za.store, ObjectStore)
32+
else:
33+
assert isinstance(za.store, LocalStore)
1834
assert zarr_path.exists()
1935
arr.open()
2036

docs/user-guide/storage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ To use the [`zarrs-python`](https://github.com/zarrs/zarrs-python) Rust implemen
3838
export CUBED_STORAGE_NAME=zarrs-python
3939
```
4040

41-
For `zarr-python` v3 only, you can use Zarr's [`ObjectStore`](https://zarr.readthedocs.io/en/main/api/zarr/storage/index.html#zarr.storage.ObjectStore) which uses the Rust-based [`obstore`](https://developmentseed.org/obstore/latest/), by setting the `storage_options.use_obstore` [configuration](../configuration.md) option to `True`, as illustrated in this YAML file:
41+
For `zarr-python`, if [`obstore`](https://developmentseed.org/obstore/latest/) is installed it will be used by default for cloud object stores. To enable it for other stores (e.g. the local filesystem), set the `storage_options.use_obstore` [configuration](../configuration.md) option to `True`, as illustrated in this YAML file:
4242

4343
```yaml
4444
spec:

0 commit comments

Comments
 (0)