Skip to content

Commit c9d4ec7

Browse files
authored
Implement new with_read_only Zarr Store method (#1021)
* Implement new `with_read_only` Zarr `Store` method This new method is not really implementable in Icechunk. If the user created a readonly session there is no real good way to turn it into a writable session, and viceversa. The implementation we are writing "lies" in this sense. It's pretending to return a new store with the requested readonly-ness, but not really. * Add FAQ on read-only
1 parent e604a97 commit c9d4ec7

File tree

3 files changed

+39
-2
lines changed

3 files changed

+39
-2
lines changed

docs/docs/icechunk-python/faq.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,10 @@ This function also accepts the filter directive. If you prefer not to use enviro
1616
```python
1717
icechunk.set_logs_filter("debug,icechunk=trace")
1818
```
19+
20+
**How to get a read-only Icechunk store?**
21+
22+
Zarr has a few mechanisms to define read-only zarr stores. These don't always work perfectly with Icechunk,
23+
because Icechunk has a more advanced session model. The safest way to make sure you don't write to
24+
an Icechunk repo is to use `Repository.readonly_session` to create the session. It doesn't matter what
25+
you do to the Zarr store, a read-only session cannot do writes.

icechunk-python/python/icechunk/store.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,16 @@ def __init__(
4242
self,
4343
store: PyStore,
4444
allow_pickling: bool,
45+
read_only: bool | None = None,
4546
*args: Any,
4647
**kwargs: Any,
4748
):
4849
"""Create a new IcechunkStore.
4950
5051
This should not be called directly, instead use the `create`, `open_existing` or `open_or_create` class methods.
5152
"""
52-
super().__init__(read_only=store.read_only)
53+
read_only = read_only if read_only is not None else store.read_only
54+
super().__init__(read_only=read_only)
5355
if store is None:
5456
raise ValueError(
5557
"An IcechunkStore should not be created with the default constructor, instead use either the create or open_existing class methods."
@@ -80,6 +82,13 @@ def __setstate__(self, state: Any) -> None:
8082
state["_read_only"] = state["_store"].read_only
8183
self.__dict__ = state
8284

85+
def with_read_only(self, read_only: bool = False) -> Store:
86+
new_store = IcechunkStore(
87+
store=self._store, allow_pickling=self._allow_pickling, read_only=read_only
88+
)
89+
new_store._is_open = False
90+
return new_store
91+
8392
@property
8493
def session(self) -> "Session":
8594
from icechunk import Session

icechunk-python/tests/test_store.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import zarr
66
from tests.conftest import parse_repo
7-
from zarr.core.buffer import default_buffer_prototype
7+
from zarr.core.buffer import cpu, default_buffer_prototype
88

99
rng = np.random.default_rng(seed=12345)
1010

@@ -74,3 +74,24 @@ def test_doesnt_support_consolidated_metadata() -> None:
7474
session = repo.writable_session("main")
7575
store = session.store
7676
assert not store.supports_consolidated_metadata
77+
78+
79+
async def test_with_readonly() -> None:
80+
repo = parse_repo("memory", "test")
81+
session = repo.readonly_session("main")
82+
store = session.store
83+
assert store.read_only
84+
85+
session = repo.writable_session("main")
86+
store = session.store
87+
writer = store.with_read_only(read_only=False)
88+
assert not writer._is_open
89+
assert not writer.read_only
90+
91+
root = zarr.group(store=store)
92+
root.create_array(name="foo", shape=(1,), chunks=(1,), dtype=np.int64)
93+
await writer.set("foo/c/0", cpu.Buffer.from_bytes(b"bar"))
94+
await writer.delete("foo/c/0")
95+
96+
reader = store.with_read_only(read_only=True)
97+
assert reader.read_only

0 commit comments

Comments
 (0)