Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
abb764e
Add _cache.py first attempt
ruaridhg Jul 31, 2025
d72078f
test.py ran without error, creating test.zarr/
ruaridhg Jul 31, 2025
e1266b4
Added testing for cache.py LRUStoreCache for v3
ruaridhg Aug 4, 2025
40e6f46
Fix ruff errors
ruaridhg Aug 4, 2025
eadc7bb
Add working example comparing LocalStore to LRUStoreCache
ruaridhg Aug 4, 2025
5f90a71
Delete test.py to clean-up
ruaridhg Aug 4, 2025
ae51d23
Added lrustorecache to changes and user-guide docs
ruaridhg Aug 7, 2025
e58329a
Fix linting issues
ruaridhg Aug 7, 2025
26bd3fc
Implement dual store cache
ruaridhg Aug 8, 2025
5c92d48
Fixed failing tests
ruaridhg Aug 8, 2025
f0c302c
Fix linting errors
ruaridhg Aug 8, 2025
11f17d6
Add logger info
ruaridhg Aug 11, 2025
a7810dc
Delete unnecessary extra functionality
ruaridhg Aug 11, 2025
a607ce0
Rename to caching_store
ruaridhg Aug 11, 2025
8e79e3e
Add test_storage.py
ruaridhg Aug 11, 2025
d31e565
Fix logic in _caching_store.py
ruaridhg Aug 11, 2025
92cd63c
Update tests to match caching_store implemtation
ruaridhg Aug 11, 2025
aa38def
Delete LRUStoreCache files
ruaridhg Aug 11, 2025
86dda09
Update __init__
ruaridhg Aug 11, 2025
bb807d0
Add functionality for max_size
ruaridhg Aug 11, 2025
ed4b284
Add tests for cache_info and clear_cache
ruaridhg Aug 11, 2025
0fe580b
Delete test.py
ruaridhg Aug 11, 2025
1d9a1f7
Fix linting errors
ruaridhg Aug 11, 2025
16ae3bd
Update feature description
ruaridhg Aug 11, 2025
62b739f
Fix errors
ruaridhg Aug 11, 2025
f51fdb8
Fix cachingstore.rst errors
ruaridhg Aug 11, 2025
ffa9822
Fix cachingstore.rst errors
ruaridhg Aug 11, 2025
cda4767
Merge branch 'main' into rmg/cache_remote_stores_locally
ruaridhg Aug 11, 2025
d20843a
Fixed eviction key logic with proper size tracking
ruaridhg Aug 11, 2025
4b8d0a6
Increase code coverage to 98%
ruaridhg Aug 11, 2025
84a87e2
Fix linting errors
ruaridhg Aug 11, 2025
f3b6b3e
Merge branch 'main' into rmg/cache_remote_stores_locally
d-v-b Aug 21, 2025
114a29a
Merge branch 'main' into rmg/cache_remote_stores_locally
d-v-b Aug 28, 2025
39cb6b1
Merge branch 'main' into rmg/cache_remote_stores_locally
d-v-b Sep 17, 2025
1f200ed
Merge branch 'main' of https://github.com/zarr-developers/zarr-python…
d-v-b Sep 30, 2025
f9c8c09
move cache store to experimental, fix bugs
d-v-b Sep 30, 2025
6861490
update changelog
d-v-b Sep 30, 2025
41d182c
remove logging config override, remove dead code, adjust evict_key lo…
d-v-b Oct 1, 2025
83539d3
add docs
d-v-b Oct 1, 2025
56db161
add tests for relaxed cache coherency
d-v-b Oct 1, 2025
3d21514
adjust code examples (but we don't know if they work, because we don'…
d-v-b Oct 1, 2025
1202fb1
update ci; don't save temporary files for cachestore; add doctest env
d-v-b Oct 1, 2025
4dd7565
add doctests
d-v-b Oct 2, 2025
b0a8c63
Merge branch 'main' of github.com:zarr-developers/zarr-python into ch…
d-v-b Oct 2, 2025
b2be131
remove test_cache_store
d-v-b Oct 2, 2025
e810306
update ci
d-v-b Oct 2, 2025
d755b2a
update ci
d-v-b Oct 2, 2025
f34446e
update ci, finally
d-v-b Oct 2, 2025
9721336
Merge branch 'main' into chore/doctests
d-v-b Oct 2, 2025
f02b539
remove unnecessary doctest script
d-v-b Oct 2, 2025
be33a9a
Merge branch 'chore/doctests' of github.com:d-v-b/zarr-python into ch…
d-v-b Oct 2, 2025
7e7bf6c
restore s3 tests
d-v-b Oct 2, 2025
4b0bca2
add s3fs dep
d-v-b Oct 2, 2025
6faa44a
Merge branch 'main' into chore/doctests
d-v-b Oct 2, 2025
e38c508
test code examples in src
d-v-b Oct 3, 2025
2c192d4
fix broken code examples
d-v-b Oct 3, 2025
ef6303e
remove ectopic changelog
d-v-b Oct 3, 2025
584acdd
make docstring code examples executible, and fix errors
d-v-b Oct 3, 2025
a897230
update async docstrings
d-v-b Oct 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,10 @@ jobs:
pip install hatch
- name: Set Up Hatch Env
run: |
hatch env create docs
hatch env run -e docs list-env
hatch run doctest:pip list
- name: Run Tests
run: |
hatch env run --env docs check
hatch run doctest:test

test-complete:
name: Test complete
Expand Down
1 change: 1 addition & 0 deletions changes/3357.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adds `zarr.experimental.cache_store.CacheStore`, a `Store` that implements caching by combining two other `Store` instances. See the [docs page](https://zarr.readthedocs.io/en/latest/user-guide/cache-store) for more information about this feature.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@d-v-b can you please remove this addition? It is already properly documented using the PR number in https://github.com/zarr-developers/zarr-python/blob/main/changes/3366.feature.md

4 changes: 4 additions & 0 deletions docs/user-guide/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ print(group)

```python exec="true" session="storage" source="above" result="ansi"
# Implicitly create a read-only FsspecStore
# Note: requires s3fs to be installed
group = zarr.open_group(
store='s3://noaa-nwm-retro-v2-zarr-pds',
mode='r',
Expand Down Expand Up @@ -59,6 +60,7 @@ print(group)

- an FSSpec URI string, indicating a [remote store](#remote-store) location:
```python exec="true" session="storage" source="above" result="ansi"
# Note: requires s3fs to be installed
group = zarr.open_group(
store='s3://noaa-nwm-retro-v2-zarr-pds',
mode='r',
Expand Down Expand Up @@ -125,6 +127,7 @@ that implements the [AbstractFileSystem](https://filesystem-spec.readthedocs.io/
API. `storage_options` can be used to configure the fsspec backend:

```python exec="true" session="storage" source="above" result="ansi"
# Note: requires s3fs to be installed
store = zarr.storage.FsspecStore.from_url(
's3://noaa-nwm-retro-v2-zarr-pds',
read_only=True,
Expand All @@ -138,6 +141,7 @@ The type of filesystem (e.g. S3, https, etc..) is inferred from the scheme of th
In case a specific filesystem is needed, one can explicitly create it. For example to create a S3 filesystem:

```python exec="true" session="storage" source="above" result="ansi"
# Note: requires s3fs to be installed
import fsspec
fs = fsspec.filesystem(
's3', anon=True, asynchronous=True,
Expand Down
15 changes: 14 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,18 @@ serve = "mkdocs serve"
build = "mkdocs build"
check = "mkdocs build --strict"
readthedocs = "rm -rf $READTHEDOCS_OUTPUT/html && cp -r site $READTHEDOCS_OUTPUT/html"

[tool.hatch.envs.doctest]
description = "Test environment for validating executable code blocks in documentation"
features = ['test', 'remote'] # Include remote dependencies for s3fs
dependencies = [
"s3fs>=2023.10.0",
"pytest",
"pytest-examples",
]

[tool.hatch.envs.doctest.scripts]
test = "pytest tests/test_docs.py -v"
list-env = "pip list"

[tool.ruff]
Expand Down Expand Up @@ -396,7 +408,8 @@ addopts = [
]
filterwarnings = [
"error",
"ignore:Unclosed client session <aiohttp.client.ClientSession.*:ResourceWarning"
"ignore:Unclosed client session <aiohttp.client.ClientSession.*:ResourceWarning",
"ignore:Numcodecs codecs are not in the Zarr version 3 specification.*:UserWarning"
]
markers = [
"asyncio: mark test as asyncio test",
Expand Down
1 change: 0 additions & 1 deletion src/zarr/experimental/cache_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ def __init__(
self.key_insert_times = {}
else:
self.key_insert_times = key_insert_times

self.cache_set_data = cache_set_data
self._cache_order = OrderedDict()
self._current_size = 0
Expand Down
108 changes: 108 additions & 0 deletions tests/test_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Tests for executable code blocks in markdown documentation.

This module uses pytest-examples to validate that all Python code examples
with exec="true" in the documentation execute successfully.
"""

from __future__ import annotations

from collections import defaultdict
from pathlib import Path

import pytest

pytest_examples = pytest.importorskip("pytest_examples")

# Find all markdown files with executable code blocks
docs_root = Path(__file__).parent.parent / "docs"


def find_markdown_files_with_exec() -> list[Path]:
"""Find all markdown files containing exec="true" code blocks."""
markdown_files = []

for md_file in docs_root.rglob("*.md"):
try:
content = md_file.read_text(encoding="utf-8")
if 'exec="true"' in content:
markdown_files.append(md_file)
except Exception:
# Skip files that can't be read
continue

return sorted(markdown_files)


def group_examples_by_session() -> list[tuple[str, str]]:
"""
Group examples by their session and file, maintaining order.

Returns a list of session_key tuples where session_key is
(file_path, session_name).
"""
all_examples = list(pytest_examples.find_examples(docs_root))

# Group by file and session
sessions = defaultdict(list)

for example in all_examples:
settings = example.prefix_settings()
if settings.get("exec") != "true":
continue

# Use file path and session name as key
file_path = example.path
session_name = settings.get("session", "_default")
session_key = (str(file_path), session_name)

sessions[session_key].append(example)

# Return sorted list of session keys for consistent test ordering
return sorted(sessions.keys(), key=lambda x: (x[0], x[1]))


def name_example(path: str, session: str) -> str:
"""Generate a readable name for a test case from file path and session."""
return f"{Path(path).relative_to(docs_root)}:{session}"


# Get all example sessions
@pytest.mark.parametrize(
"session_key", group_examples_by_session(), ids=lambda v: name_example(v[0], v[1])
)
def test_documentation_examples(
session_key: tuple[str, str],
eval_example: pytest_examples.EvalExample, # type: ignore[name-defined]
) -> None:
"""
Test that all exec="true" code examples in documentation execute successfully.

This test groups examples by session (file + session name) and runs them
sequentially in the same execution context, allowing code to build on
previous examples.

This test uses pytest-examples to:
- Find all code examples with exec="true" in markdown files
- Group them by session
- Execute them in order within the same context
- Verify no exceptions are raised
"""
file_path, session_name = session_key

# Get examples for this session
all_examples = list(pytest_examples.find_examples(docs_root))
examples = []
for example in all_examples:
settings = example.prefix_settings()
if settings.get("exec") != "true":
continue
if str(example.path) == file_path and settings.get("session", "_default") == session_name:
examples.append(example)

# Run all examples in this session sequentially, preserving state
module_globals: dict[str, object] = {}
for example in examples:
result = eval_example.run(example, module_globals=module_globals)
# Update globals with the results from this execution
module_globals.update(result)