rustytree-xarray 0.2.1__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/.github/workflows/ci.yml +31 -4
  2. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/CHANGELOG.md +185 -1
  3. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/Cargo.lock +78 -40
  4. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/Cargo.toml +33 -4
  5. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/PKG-INFO +5 -4
  6. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/README.md +1 -1
  7. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/docs/usage.md +67 -10
  8. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/notebooks/klot_demo.ipynb +289 -299
  9. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/pyproject.toml +17 -3
  10. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/python/rustytree/__init__.py +1 -1
  11. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/python/rustytree/backend.py +128 -57
  12. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/array.rs +86 -2
  13. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/error.rs +0 -7
  14. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/icechunk_store.rs +5 -0
  15. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/lib.rs +52 -11
  16. rustytree_xarray-0.3.0/src/py_credentials.rs +393 -0
  17. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/runtime.rs +10 -0
  18. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/store.rs +96 -1
  19. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/walk.rs +59 -6
  20. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/conftest.py +3 -3
  21. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/test_backend_entrypoint.py +237 -75
  22. rustytree_xarray-0.3.0/tests/test_codecs.py +80 -0
  23. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/test_icechunk.py +39 -0
  24. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/test_phase1_scaffold.py +1 -1
  25. rustytree_xarray-0.3.0/tests/test_pickle.py +137 -0
  26. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/test_to_rust_source.py +84 -1
  27. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/.github/workflows/release.yml +0 -0
  28. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/.gitignore +0 -0
  29. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/LICENSE +0 -0
  30. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/assets/logo-banner-dark.png +0 -0
  31. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/assets/logo-banner-dark.svg +0 -0
  32. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/assets/logo-banner-light.png +0 -0
  33. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/assets/logo-banner-light.svg +0 -0
  34. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/assets/logo.png +0 -0
  35. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/docs/architecture.md +0 -0
  36. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/docs/contributing.md +0 -0
  37. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/docs/release-process.md +0 -0
  38. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/python/rustytree/_array.py +0 -0
  39. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/dtype_dispatch.rs +0 -0
  40. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/glob.rs +0 -0
  41. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/node.rs +0 -0
  42. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/src/url.rs +0 -0
  43. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/test_chunks.py +0 -0
  44. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/test_eager_fetch.py +0 -0
  45. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/test_lazy.py +0 -0
  46. {rustytree_xarray-0.2.1 → rustytree_xarray-0.3.0}/tests/test_walk.py +0 -0
@@ -15,7 +15,22 @@ env:
15
15
  RUSTFLAGS: -D warnings
16
16
 
17
17
  jobs:
18
- # Cargo gates run once on stable Rust. Cheap; no Python needed.
18
+ # Python lint + format gate. Fast and dependency-free (ruff is a standalone
19
+ # binary; no extension build needed), so it runs in parallel with the cargo
20
+ # and pytest jobs and fails fast on style drift. The pinned version must
21
+ # match the `ruff` pin in pyproject's `dev` extra so local `ruff format`
22
+ # and this check agree.
23
+ lint:
24
+ name: ruff (lint + format)
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - uses: astral-sh/setup-uv@v4
29
+ - run: uvx 'ruff@0.15.8' check .
30
+ - run: uvx 'ruff@0.15.8' format --check .
31
+
32
+ # Cargo gates run once on stable Rust. `cargo test` links libpython (see the
33
+ # test step), so Python is set up here too.
19
34
  cargo:
20
35
  name: cargo (fmt, clippy, test)
21
36
  runs-on: ubuntu-latest
@@ -24,10 +39,21 @@ jobs:
24
39
  - uses: dtolnay/rust-toolchain@stable
25
40
  with:
26
41
  components: clippy, rustfmt
42
+ - uses: actions/setup-python@v5
43
+ with:
44
+ python-version: "3.12"
27
45
  - uses: Swatinem/rust-cache@v2
28
46
  - run: cargo fmt --check
29
47
  - run: cargo clippy --all-targets --all-features --locked -- -D warnings
30
- - run: cargo test --locked
48
+ # Build tests WITHOUT `extension-module` so PyO3 links libpython: the
49
+ # credential-fetcher shim's `#[typetag::serde]` impls are force-retained
50
+ # by `inventory` and call `Python::attach`, so the test binary references
51
+ # libpython symbols that can't be dead-code-eliminated (they can under
52
+ # `extension-module`, which suppresses libpython linking for the cdylib).
53
+ - name: cargo test
54
+ run: cargo test --locked --no-default-features
55
+ env:
56
+ LD_LIBRARY_PATH: ${{ env.pythonLocation }}/lib
31
57
 
32
58
  # Python integration: build the cdylib via maturin and run pytest against
33
59
  # the supported interpreter range. Each matrix cell rebuilds the extension
@@ -59,8 +85,9 @@ jobs:
59
85
  # Install the [dev] extras directly rather than `pip install -e ".[dev]"`,
60
86
  # which would re-trigger a full cdylib rebuild on top of maturin develop
61
87
  # below (we'd build the wheel twice — saves ~80s per matrix cell).
62
- # Keep this list in sync with the `dev` extra in pyproject.toml.
63
- run: uv pip install --python .venv/bin/python pytest pytest-cov 'zarr>=3.0' 'icechunk>=2.0.5' ruff
88
+ # These are the deps pytest needs; ruff is not among them — linting runs
89
+ # in the dedicated `lint` job above.
90
+ run: uv pip install --python .venv/bin/python pytest pytest-cov 'zarr>=3.0' 'icechunk>=2.1.0,<2.2' 'dask[distributed]>=2024.1'
64
91
  - name: Build extension (maturin develop)
65
92
  run: .venv/bin/maturin develop
66
93
  - name: pytest
@@ -11,6 +11,180 @@ release, that section is renamed to `[x.y.z] - YYYY-MM-DD` and a fresh
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.3.0] - 2026-07-04
15
+
16
+ ### Added
17
+
18
+ - `group_filter` keyword for `open_datatree` ([#49]). An
19
+ `fnmatch`-style glob matched against every group path — only matching groups
20
+ (plus their ancestors and the root, so the tree stays connected) are loaded.
21
+ Mirrors xarray PR [pydata/xarray#11302](https://github.com/pydata/xarray/pull/11302):
22
+ matching follows `PurePosixPath.match` (right-anchored), so
23
+ `group_filter="*/sweep_0"` opens the lowest sweep of every volume, and group
24
+ names that literally contain glob metacharacters are reachable via character-
25
+ class escapes (`[*]`, `[?]`, `[[]`). Mutually exclusive with `group`. The Rust
26
+ walk prunes non-matching subtrees up front (conservative prefix predicate);
27
+ Python's `_filter_by_glob` remains the source of truth. `open_dataset` rejects
28
+ `group_filter` (a single Dataset has no defined target for a multi-match glob).
29
+
30
+ - Docs: a "Distributed compute (`dask.distributed` / Coiled)" section in
31
+ `docs/usage.md` ([#46]) covering the requirements for a remote cluster
32
+ (`spawn` workers, matching `rustytree` + `icechunk` in the worker environment)
33
+ and — mirroring icechunk's own guidance — how to avoid shipping secrets to
34
+ workers: use `from_env` / `refreshable` / `anonymous` credentials (secret-free
35
+ in the task graph) rather than `static` keys. Verified end-to-end that a
36
+ `from_env` session opened via rustytree pickles with no secret in the graph and
37
+ still computes on a distributed cluster.
38
+
39
+ - Picklable array handles for `dask.distributed` ([#44], fixes #44).
40
+ DataArrays opened with `engine="rustytree"` could not be computed under a
41
+ `dask.distributed` cluster — `.compute()` failed while distributed pickled
42
+ the task graph, because each lazy chunk held a native
43
+ `ZarrsArrayHandle` that raised `TypeError: cannot pickle
44
+ 'rustytree._rustytree.ZarrsArrayHandle' object`. `ZarrsArrayHandle` now
45
+ implements `__reduce__`: for stores opened via an icechunk `Session` (both
46
+ local-filesystem and remote S3, the cases in #44) the handle carries the
47
+ session's own `as_bytes()` msgpack plus the array path, and a worker revives
48
+ it via icechunk's `Session::from_bytes` + `Array::async_open`. This mirrors
49
+ icechunk exactly — rustytree adds **no** credential handling of its own, so
50
+ credential exposure is precisely icechunk's: `from_env` / `anonymous` sessions
51
+ carry no secret into the task graph, while **static** credentials (and a
52
+ refreshable session's scattered `initial` creds) are embedded in the session
53
+ bytes and travel to every worker in the pickled graph — the same as
54
+ distributing an icechunk Session directly. Opening through an icechunk Session
55
+ with `from_env` / `anonymous` / `refreshable` credentials is the recommended
56
+ distributed pattern. Workers must use the `spawn` start-method (the
57
+ `dask.distributed` default); rustytree's tokio runtime is not fork-safe, so
58
+ fork-based multiprocessing after opening a store is unsupported. Vanilla
59
+ `s3://` / local Zarr stores are not yet picklable (no icechunk Session to
60
+ mirror) and now raise a clear, actionable error at pickle time instead of the
61
+ opaque default. New `tests/test_pickle.py` covers the pickle round-trip
62
+ (including deeply-nested arrays and multi-hop re-pickling), the not-picklable
63
+ and corrupt-state error paths, and an opt-in `distributed`-marked
64
+ `LocalCluster` compute.
65
+
66
+ - `numcodecs.zlib` codec support ([#41], fixes #42). Enables the `zlib`
67
+ feature on the `zarrs` dependency so rustytree can decode arrays whose
68
+ codec pipeline uses the non-standard `numcodecs.`-namespace codecs
69
+ zarr-python writes — e.g. the public `earthmover-public/goes-16`
70
+ arraylake dataset (and the ismip6 icechunk store reported in #42, where
71
+ enabling it gave a ~10× speedup over `engine="zarr"`), which chain
72
+ `bytes → numcodecs.shuffle → numcodecs.zlib`. zarrs's `zlib` codec is
73
+ documented byte-compatible
74
+ with zarr-python's; `numcodecs.shuffle` is always compiled, and
75
+ `gzip` / `blosc` / `zstd` / `crc32c` already come from zarrs's
76
+ default features. Verified bit-for-bit against zarr-python on a
77
+ GOES-16 `CMI_C01` slice. New non-gated `tests/test_codecs.py` writes
78
+ the shuffle+zlib pipeline to a local store and checks the decode
79
+ round-trips.
80
+
81
+ ### Changed
82
+
83
+ - CI now runs `ruff check` and `ruff format --check` in a dedicated `lint` job,
84
+ and `ruff` is pinned (`>=0.15.8,<0.16`) in the `dev` extra so local and CI
85
+ formatting agree ([#50], fixes #48). Previously CI installed `ruff` but never
86
+ invoked it, so lint/format drift accumulated silently on `main`; this normalises
87
+ the drifted files (`backend.py`, `conftest.py`, `test_backend_entrypoint.py`,
88
+ `test_codecs.py`, `klot_demo.ipynb`) in one no-logic-change pass and gates
89
+ against recurrence. No runtime behaviour change.
90
+
91
+ - **Breaking:** `group=` is now exact-path only and no longer auto-detects glob
92
+ patterns ([#49]). Previously a `group=` value containing `*`, `?`, or `[`
93
+ silently switched to glob filtering; that overloaded behaviour is removed in
94
+ favour of the explicit `group_filter=` (see Added). This aligns with the final
95
+ design of xarray PR [pydata/xarray#11302](https://github.com/pydata/xarray/pull/11302),
96
+ which rejected the ambiguous overloaded form because group names may legitimately
97
+ contain glob metacharacters. Migrate `open_datatree(group="*/sweep_0")` to
98
+ `open_datatree(group_filter="*/sweep_0")`; `group` and `group_filter` are
99
+ mutually exclusive (passing both raises `ValueError`). A `group=` value with
100
+ glob characters is now looked up as a literal path.
101
+
102
+ - Docs: tidy the `notebooks/klot_demo.ipynb` demo — trim WHAT-narrating comments
103
+ and add an `xradar` prerequisite note ([#47]). No code change.
104
+
105
+ - Bump the pinned `icechunk` from 2.0.5 to 2.1.0 ([#46]). rustytree links the
106
+ `icechunk` Rust crate and round-trips sessions through
107
+ `Session::{as,from}_bytes`, so it can only open stores whose on-disk format
108
+ matches its pinned icechunk — 2.0.5 could not open repositories written by
109
+ icechunk 2.0.6+, failing in `Repository::open` with "the repository doesn't
110
+ exist" even though the store was present and the credentials valid. Bumping to
111
+ 2.1.0 restores access to current icechunk stores; `typetag` moves to `=0.2.22`
112
+ to keep sharing icechunk's `inventory` credential-fetcher registry (the #41
113
+ `py_credentials` shim). The `dev` extra and CI now require
114
+ `icechunk>=2.1.0,<2.2` so the Python and Rust icechunk versions share a minor —
115
+ the msgpack session-bytes format is coupled across the FFI boundary, and a skew
116
+ is exactly what produced the "repository doesn't exist" failure. No rustytree
117
+ source changes were needed (the API surface is unchanged). Verified end-to-end
118
+ against a real credentialed AWS S3 icechunk 2.0.6 store: parity with
119
+ `engine="zarr"`, pickle round-trip, and a distributed `LocalCluster` compute
120
+ whose workers reopen the store from the pickled session.
121
+
122
+ ### Fixed
123
+
124
+ - Unified the missing-group error type across backends ([#52], fixes #51).
125
+ A literal `group=` path that doesn't exist now raises `KeyError` on both
126
+ vanilla Zarr and icechunk stores — previously vanilla raised `RuntimeError`
127
+ (from the Rust walk) while icechunk raised `KeyError`, so callers couldn't
128
+ `except` one type. The Rust walk now maps zarrs' `GroupCreateError::MissingMetadata`
129
+ to `RustytreeError::NotFound` (→ `PyKeyError`); genuine storage/IO/corruption
130
+ errors still surface as `RuntimeError`.
131
+
132
+ - `open_dataset` and `open_datatree` now validate the `group`/`group_filter`
133
+ pair consistently ([#52]). Passing both to `open_dataset` raises the same
134
+ `ValueError` (mutually exclusive) as `open_datatree` instead of a
135
+ `NotImplementedError`; the shared validator (renamed
136
+ `_check_group_filter_mutex` → `_validate_group_filter` since it also rejects
137
+ an empty `group_filter`) runs first on both entry points.
138
+
139
+ - Docs and the `klot_demo` notebook still used the removed glob-`group=`
140
+ form (`group="*/sweep_0"`) that [#49] turned into a literal-path lookup —
141
+ the examples would now raise instead of filtering. Migrated `README.md`,
142
+ `docs/usage.md`, and `notebooks/klot_demo.ipynb` to `group_filter=`
143
+ ([#50]).
144
+
145
+ - Spurious `SerializationWarning: variable '...' has multiple fill values`
146
+ and silently-broken masking for stores whose `_FillValue` is written in
147
+ raw Zarr wire form ([#43]). Some virtual/icechunk stores (e.g. the
148
+ source.coop ISMIP6 dataset) carry `_FillValue` as a base64-encoded
149
+ string (`'AAAAgB2vFUQ='`) alongside a numeric `missing_value`; xarray's
150
+ CF mask coder saw two distinct sentinels for the same value and warned,
151
+ and the base64 string masked nothing (a float array never equals a
152
+ `str`), so masking quietly fell back to `missing_value` alone.
153
+ `_RustyDataStore.get_variables` now mirrors xarray's zarr backend
154
+ (`ZarrStore.open_store_variable`): a base64 `str`/`bytes` `_FillValue`
155
+ is decoded to a numeric sentinel via `FillValueCoder.decode`, while an
156
+ already-numeric fill is left untouched. Verified byte-for-byte against
157
+ `engine="zarr"` on the ISMIP6 store (warnings 28 → 0, identical
158
+ `_FillValue` encoding and masking). Non-numeric list/tuple wire forms
159
+ (e.g. a complex `[real, imag]` fill) have no decoder branch and pass
160
+ through unchanged rather than raising. Network-free regression tests
161
+ cover the base64 decode, numeric pass-through, and list pass-through.
162
+
163
+ - Open arraylake / Earthmover icechunk sessions ([#41], fixes #40).
164
+ `xr.open_datatree(session.store, engine="rustytree")` raised
165
+ `ValueError: icechunk session: unknown error: unknown variant
166
+ PythonCredentialsFetcher, there are no variants` for sessions
167
+ created via arraylake. Such sessions store S3 credentials as
168
+ `Refreshable(Arc<dyn S3CredentialsFetcher>)` whose concrete fetcher
169
+ is `#[typetag::serde]`-registered only inside icechunk-python's
170
+ cdylib; rustytree links the vanilla `icechunk` crate, whose fetcher
171
+ registry is empty, so `Session::from_bytes` couldn't resolve the tag.
172
+ `xr.open_zarr` is unaffected because it uses the live in-process
173
+ store and never round-trips through bytes. New `src/py_credentials.rs`
174
+ re-registers `PythonCredentialsFetcher` (S3 / GCS / Azure) inside
175
+ rustytree's cdylib, mirroring icechunk-python's `get()`: serve the
176
+ scattered `initial` static credentials while fresh, else acquire the
177
+ GIL and re-run the embedded pickled credential callable to refresh —
178
+ so the common case and `scatter_initial_credentials=False` /
179
+ mid-walk credential expiry all keep working. `typetag` is pinned
180
+ `=0.2.21` to share icechunk's `inventory` registry. A Python-side
181
+ friendly error (`_rust_open_or_explain` in `backend.py`) is a safety
182
+ net for a future icechunk-python that renames the fetcher. CI now
183
+ builds tests with `--no-default-features` so PyO3 links libpython:
184
+ the inventory-retained fetcher impls reference `Python::attach`, so
185
+ their libpython symbols can no longer be dead-code-eliminated from
186
+ the test binary.
187
+
14
188
  ## [0.2.1] - 2026-05-23
15
189
 
16
190
  ### Fixed
@@ -530,7 +704,9 @@ below.
530
704
  intentionally not normalized — relative-vs-absolute glob
531
705
  semantics differ in `PurePosixPath.match`.
532
706
 
533
- [Unreleased]: https://github.com/aladinor/rustytree/compare/v0.2.0...HEAD
707
+ [Unreleased]: https://github.com/aladinor/rustytree/compare/v0.3.0...HEAD
708
+ [0.3.0]: https://github.com/aladinor/rustytree/compare/v0.2.1...v0.3.0
709
+ [0.2.1]: https://github.com/aladinor/rustytree/compare/v0.2.0...v0.2.1
534
710
  [0.2.0]: https://github.com/aladinor/rustytree/compare/v0.1.0...v0.2.0
535
711
  [0.1.0]: https://github.com/aladinor/rustytree/releases/tag/v0.1.0
536
712
  [#1]: https://github.com/aladinor/rustytree/pull/1
@@ -558,3 +734,11 @@ below.
558
734
  [#25]: https://github.com/aladinor/rustytree/pull/25
559
735
  [#26]: https://github.com/aladinor/rustytree/pull/26
560
736
  [#27]: https://github.com/aladinor/rustytree/pull/27
737
+ [#41]: https://github.com/aladinor/rustytree/pull/41
738
+ [#43]: https://github.com/aladinor/rustytree/pull/43
739
+ [#44]: https://github.com/aladinor/rustytree/pull/44
740
+ [#46]: https://github.com/aladinor/rustytree/pull/46
741
+ [#47]: https://github.com/aladinor/rustytree/pull/47
742
+ [#49]: https://github.com/aladinor/rustytree/pull/49
743
+ [#50]: https://github.com/aladinor/rustytree/pull/50
744
+ [#52]: https://github.com/aladinor/rustytree/pull/52
@@ -505,15 +505,16 @@ dependencies = [
505
505
 
506
506
  [[package]]
507
507
  name = "aws-smithy-runtime"
508
- version = "1.11.1"
508
+ version = "1.11.3"
509
509
  source = "registry+https://github.com/rust-lang/crates.io-index"
510
- checksum = "0504b1ab12debb5959e5165ee5fe97dd387e7aa7ea6a477bfd7635dfe769a4f5"
510
+ checksum = "b8e6f5caf6fea86f8c2206541ab5857cfcda9013426cdbe8fa0098b9e2d32182"
511
511
  dependencies = [
512
512
  "aws-smithy-async",
513
513
  "aws-smithy-http",
514
514
  "aws-smithy-http-client",
515
515
  "aws-smithy-observability",
516
516
  "aws-smithy-runtime-api",
517
+ "aws-smithy-schema",
517
518
  "aws-smithy-types",
518
519
  "bytes",
519
520
  "fastrand",
@@ -530,9 +531,9 @@ dependencies = [
530
531
 
531
532
  [[package]]
532
533
  name = "aws-smithy-runtime-api"
533
- version = "1.12.0"
534
+ version = "1.12.3"
534
535
  source = "registry+https://github.com/rust-lang/crates.io-index"
535
- checksum = "b71a13df6ada0aafbf21a73bdfcdf9324cfa9df77d96b8446045be3cde61b42e"
536
+ checksum = "9db177daa6ba8afb9ee1aefcf548c907abcf52065e394ee11a92780057fe0e8c"
536
537
  dependencies = [
537
538
  "aws-smithy-async",
538
539
  "aws-smithy-runtime-api-macros",
@@ -557,11 +558,22 @@ dependencies = [
557
558
  "syn 2.0.117",
558
559
  ]
559
560
 
561
+ [[package]]
562
+ name = "aws-smithy-schema"
563
+ version = "0.1.0"
564
+ source = "registry+https://github.com/rust-lang/crates.io-index"
565
+ checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5"
566
+ dependencies = [
567
+ "aws-smithy-runtime-api",
568
+ "aws-smithy-types",
569
+ "http 1.4.0",
570
+ ]
571
+
560
572
  [[package]]
561
573
  name = "aws-smithy-types"
562
- version = "1.4.7"
574
+ version = "1.5.0"
563
575
  source = "registry+https://github.com/rust-lang/crates.io-index"
564
- checksum = "9d73dbfbaa8e4bc57b9045137680b958d274823509a360abfd8e1d514d40c95c"
576
+ checksum = "32b42fcf341259d85ca10fac9a2f6448a8ec691c6955a18e45bc3b71a85fab85"
565
577
  dependencies = [
566
578
  "base64-simd",
567
579
  "bytes",
@@ -706,6 +718,15 @@ dependencies = [
706
718
  "zstd-sys",
707
719
  ]
708
720
 
721
+ [[package]]
722
+ name = "bs58"
723
+ version = "0.5.1"
724
+ source = "registry+https://github.com/rust-lang/crates.io-index"
725
+ checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
726
+ dependencies = [
727
+ "tinyvec",
728
+ ]
729
+
709
730
  [[package]]
710
731
  name = "bumpalo"
711
732
  version = "3.20.2"
@@ -1846,9 +1867,9 @@ dependencies = [
1846
1867
 
1847
1868
  [[package]]
1848
1869
  name = "icechunk"
1849
- version = "2.0.5"
1870
+ version = "2.1.0"
1850
1871
  source = "registry+https://github.com/rust-lang/crates.io-index"
1851
- checksum = "7af416f5b28f3bea35f2d48dd018b7cba711c35a1c965311782222ac337bbccd"
1872
+ checksum = "08a02fb61448609885f19dd8e9ffb0909792a59f1b36599ddb3c0d35f7090fef"
1852
1873
  dependencies = [
1853
1874
  "async-compression",
1854
1875
  "async-recursion",
@@ -1864,7 +1885,7 @@ dependencies = [
1864
1885
  "icechunk-s3",
1865
1886
  "icechunk-storage",
1866
1887
  "icechunk-types",
1867
- "itertools",
1888
+ "itertools 0.15.0",
1868
1889
  "quick_cache",
1869
1890
  "rand 0.10.1",
1870
1891
  "regex",
@@ -1887,14 +1908,15 @@ dependencies = [
1887
1908
 
1888
1909
  [[package]]
1889
1910
  name = "icechunk-arrow-object-store"
1890
- version = "2.0.5"
1911
+ version = "2.1.0"
1891
1912
  source = "registry+https://github.com/rust-lang/crates.io-index"
1892
- checksum = "8e7610964a5f7859731c95eaec642fcf839e5467137da10d9a2a477c47c1ca2b"
1913
+ checksum = "ceb90dfc6ec56c155ec98338c726660d5398fa5fda450bde7f63145a9e1e22ac"
1893
1914
  dependencies = [
1894
1915
  "async-trait",
1895
1916
  "bytes",
1896
1917
  "chrono",
1897
1918
  "futures",
1919
+ "http 1.4.0",
1898
1920
  "icechunk-storage",
1899
1921
  "icechunk-types",
1900
1922
  "object_store",
@@ -1909,9 +1931,9 @@ dependencies = [
1909
1931
 
1910
1932
  [[package]]
1911
1933
  name = "icechunk-format"
1912
- version = "2.0.5"
1934
+ version = "2.1.0"
1913
1935
  source = "registry+https://github.com/rust-lang/crates.io-index"
1914
- checksum = "258c49d6a1b89fab075fa8b9c31829861ec52f92e5984ab924f4ad834ede13a6"
1936
+ checksum = "d1e8bbd643e33147b3850ba9044981990998cb59c9f0bc5d274220c526c15536"
1915
1937
  dependencies = [
1916
1938
  "base32",
1917
1939
  "bytes",
@@ -1920,7 +1942,7 @@ dependencies = [
1920
1942
  "flexbuffers",
1921
1943
  "futures",
1922
1944
  "icechunk-types",
1923
- "itertools",
1945
+ "itertools 0.15.0",
1924
1946
  "quick_cache",
1925
1947
  "rand 0.10.1",
1926
1948
  "rmp-serde",
@@ -1935,9 +1957,9 @@ dependencies = [
1935
1957
 
1936
1958
  [[package]]
1937
1959
  name = "icechunk-s3"
1938
- version = "2.0.5"
1960
+ version = "2.1.0"
1939
1961
  source = "registry+https://github.com/rust-lang/crates.io-index"
1940
- checksum = "79e08bc924fcc375bf6a2214cc897c09d46b242821999f6c6e3eb87adb0fb0a4"
1962
+ checksum = "e381c4d1f80a1f89e5d3175120700ed72a11a238a62a14e41b259ba38e169353"
1941
1963
  dependencies = [
1942
1964
  "async-trait",
1943
1965
  "aws-config",
@@ -1960,16 +1982,16 @@ dependencies = [
1960
1982
 
1961
1983
  [[package]]
1962
1984
  name = "icechunk-storage"
1963
- version = "2.0.5"
1985
+ version = "2.1.0"
1964
1986
  source = "registry+https://github.com/rust-lang/crates.io-index"
1965
- checksum = "d8341b2ce1729d586b9460af93c5c0549d2937df48ad7407101498f56ce99d75"
1987
+ checksum = "8f1e267f2a4d7749bf37766764b8b68a098079609aed98296efa2b3255fb577c"
1966
1988
  dependencies = [
1967
1989
  "async-trait",
1968
1990
  "bytes",
1969
1991
  "chrono",
1970
1992
  "futures",
1971
1993
  "icechunk-types",
1972
- "itertools",
1994
+ "itertools 0.15.0",
1973
1995
  "serde",
1974
1996
  "thiserror",
1975
1997
  "tokio",
@@ -1981,9 +2003,9 @@ dependencies = [
1981
2003
 
1982
2004
  [[package]]
1983
2005
  name = "icechunk-types"
1984
- version = "2.0.5"
2006
+ version = "2.1.0"
1985
2007
  source = "registry+https://github.com/rust-lang/crates.io-index"
1986
- checksum = "3ae8062e3d19227364ba07bd13f2b3bfbb33beaf46883b72b26f36d0ae359449"
2008
+ checksum = "3fd025f9b9e34ec544f50bf2bbb0b9ea5bd2b9b90f165e473c57c4f6227cfc6c"
1987
2009
  dependencies = [
1988
2010
  "serde",
1989
2011
  "serde_with",
@@ -2164,6 +2186,15 @@ dependencies = [
2164
2186
  "either",
2165
2187
  ]
2166
2188
 
2189
+ [[package]]
2190
+ name = "itertools"
2191
+ version = "0.15.0"
2192
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2193
+ checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
2194
+ dependencies = [
2195
+ "either",
2196
+ ]
2197
+
2167
2198
  [[package]]
2168
2199
  name = "itoa"
2169
2200
  version = "1.0.18"
@@ -2582,7 +2613,7 @@ dependencies = [
2582
2613
  "httparse",
2583
2614
  "humantime",
2584
2615
  "hyper 1.9.0",
2585
- "itertools",
2616
+ "itertools 0.14.0",
2586
2617
  "md-5 0.10.6",
2587
2618
  "parking_lot",
2588
2619
  "percent-encoding",
@@ -2796,6 +2827,7 @@ version = "0.28.3"
2796
2827
  source = "registry+https://github.com/rust-lang/crates.io-index"
2797
2828
  checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
2798
2829
  dependencies = [
2830
+ "chrono",
2799
2831
  "libc",
2800
2832
  "once_cell",
2801
2833
  "portable-atomic",
@@ -2860,9 +2892,9 @@ dependencies = [
2860
2892
 
2861
2893
  [[package]]
2862
2894
  name = "quick_cache"
2863
- version = "0.6.21"
2895
+ version = "0.6.24"
2864
2896
  source = "registry+https://github.com/rust-lang/crates.io-index"
2865
- checksum = "5a70b1b8b47e31d0498ecbc3c5470bb931399a8bfed1fd79d1717a61ce7f96e3"
2897
+ checksum = "b9c6658afe513a3b484e3abfdaa0d03ef3c0bbf017542c178dd55f94eb3051f9"
2866
2898
  dependencies = [
2867
2899
  "ahash",
2868
2900
  "equivalent",
@@ -3346,15 +3378,20 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
3346
3378
 
3347
3379
  [[package]]
3348
3380
  name = "rustytree"
3349
- version = "0.2.1"
3381
+ version = "0.3.0"
3350
3382
  dependencies = [
3383
+ "async-trait",
3384
+ "chrono",
3351
3385
  "futures",
3352
3386
  "icechunk",
3353
3387
  "numpy",
3354
3388
  "pyo3",
3389
+ "rmp-serde",
3390
+ "serde",
3355
3391
  "serde_json",
3356
3392
  "thiserror",
3357
3393
  "tokio",
3394
+ "typetag",
3358
3395
  "zarrs",
3359
3396
  "zarrs_icechunk",
3360
3397
  "zarrs_object_store",
@@ -3510,9 +3547,9 @@ dependencies = [
3510
3547
 
3511
3548
  [[package]]
3512
3549
  name = "serde_json"
3513
- version = "1.0.149"
3550
+ version = "1.0.150"
3514
3551
  source = "registry+https://github.com/rust-lang/crates.io-index"
3515
- checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
3552
+ checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
3516
3553
  dependencies = [
3517
3554
  "indexmap 2.14.0",
3518
3555
  "itoa",
@@ -3547,11 +3584,12 @@ dependencies = [
3547
3584
 
3548
3585
  [[package]]
3549
3586
  name = "serde_with"
3550
- version = "3.19.0"
3587
+ version = "3.21.0"
3551
3588
  source = "registry+https://github.com/rust-lang/crates.io-index"
3552
- checksum = "f05839ce67618e14a09b286535c0d9c94e85ef25469b0e13cb4f844e5593eb19"
3589
+ checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c"
3553
3590
  dependencies = [
3554
3591
  "base64",
3592
+ "bs58",
3555
3593
  "chrono",
3556
3594
  "hex",
3557
3595
  "indexmap 1.9.3",
@@ -3566,9 +3604,9 @@ dependencies = [
3566
3604
 
3567
3605
  [[package]]
3568
3606
  name = "serde_with_macros"
3569
- version = "3.19.0"
3607
+ version = "3.21.0"
3570
3608
  source = "registry+https://github.com/rust-lang/crates.io-index"
3571
- checksum = "cf2ebbe86054f9b45bc3881e865683ccfaccce97b9b4cb53f3039d67f355a334"
3609
+ checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
3572
3610
  dependencies = [
3573
3611
  "darling",
3574
3612
  "proc-macro2",
@@ -3928,9 +3966,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
3928
3966
 
3929
3967
  [[package]]
3930
3968
  name = "tokio"
3931
- version = "1.52.2"
3969
+ version = "1.52.3"
3932
3970
  source = "registry+https://github.com/rust-lang/crates.io-index"
3933
- checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386"
3971
+ checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
3934
3972
  dependencies = [
3935
3973
  "bytes",
3936
3974
  "libc",
@@ -4126,9 +4164,9 @@ checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
4126
4164
 
4127
4165
  [[package]]
4128
4166
  name = "typetag"
4129
- version = "0.2.21"
4167
+ version = "0.2.22"
4130
4168
  source = "registry+https://github.com/rust-lang/crates.io-index"
4131
- checksum = "be2212c8a9b9bcfca32024de14998494cf9a5dfa59ea1b829de98bac374b86bf"
4169
+ checksum = "c5a897b12c6c1151ad0b138b8db50252dc301f93bc3b027db05eec82aeed298c"
4132
4170
  dependencies = [
4133
4171
  "erased-serde",
4134
4172
  "inventory",
@@ -4139,9 +4177,9 @@ dependencies = [
4139
4177
 
4140
4178
  [[package]]
4141
4179
  name = "typetag-impl"
4142
- version = "0.2.21"
4180
+ version = "0.2.22"
4143
4181
  source = "registry+https://github.com/rust-lang/crates.io-index"
4144
- checksum = "27a7a9b72ba121f6f1f6c3632b85604cac41aedb5ddc70accbebb6cac83de846"
4182
+ checksum = "cf808357c6ed7e13ba0f3277ec8d8f21b2d501274895104263985330c726c1c5"
4145
4183
  dependencies = [
4146
4184
  "proc-macro2",
4147
4185
  "quote",
@@ -4827,7 +4865,7 @@ dependencies = [
4827
4865
  "getrandom 0.3.4",
4828
4866
  "half",
4829
4867
  "inventory",
4830
- "itertools",
4868
+ "itertools 0.14.0",
4831
4869
  "itoa",
4832
4870
  "log",
4833
4871
  "lru",
@@ -4877,7 +4915,7 @@ checksum = "270efeb0181651aee5460b3232f2fc83e91bd646cefe75001d1c8f9a4f3abf81"
4877
4915
  dependencies = [
4878
4916
  "bytes",
4879
4917
  "derive_more",
4880
- "itertools",
4918
+ "itertools 0.14.0",
4881
4919
  "libc",
4882
4920
  "page_size",
4883
4921
  "pathdiff",
@@ -4974,7 +5012,7 @@ dependencies = [
4974
5012
  "bytes",
4975
5013
  "derive_more",
4976
5014
  "futures",
4977
- "itertools",
5015
+ "itertools 0.14.0",
4978
5016
  "thiserror",
4979
5017
  "unsafe_cell_slice",
4980
5018
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rustytree"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  edition = "2024"
5
5
  rust-version = "1.91.1"
6
6
  license = "Apache-2.0"
@@ -22,12 +22,27 @@ default = ["extension-module"]
22
22
  extension-module = ["pyo3/extension-module"]
23
23
 
24
24
  [dependencies]
25
- pyo3 = { version = "0.28" }
25
+ # `chrono` feature lets us extract credential `expires_after` (a Python
26
+ # `datetime`) straight into `DateTime<Utc>` in the credential-fetcher shim
27
+ # (`src/py_credentials.rs`).
28
+ pyo3 = { version = "0.28", features = ["chrono"] }
26
29
  numpy = "0.28"
27
30
  tokio = { version = "1", features = ["rt-multi-thread", "sync"] }
28
31
  futures = "0.3"
29
32
  thiserror = "2"
30
33
 
34
+ # Credential-fetcher shim (`src/py_credentials.rs`): re-register icechunk-python's
35
+ # `PythonCredentialsFetcher` typetag inside our cdylib so `Session::from_bytes`
36
+ # can deserialize arraylake/Earthmover sessions. `typetag` MUST match the version
37
+ # icechunk links (`=0.2.22` for icechunk 2.1.x) so the `inventory` registry is
38
+ # shared and our impls are found by icechunk's `Session` deserializer; a skew
39
+ # silently splits the registry. `chrono` unifies to icechunk's 0.4.x so
40
+ # `DateTime<Utc>` types match.
41
+ typetag = "=0.2.22"
42
+ async-trait = "0.1"
43
+ chrono = "0.4"
44
+ serde = { version = "1", features = ["derive"] }
45
+
31
46
  # Zarr v3 reader stack. Remote object_store backends (S3/GCS/Azure/HTTP)
32
47
  # land in a follow-up; this PR ships local-filesystem only for both the
33
48
  # vanilla and icechunk paths.
@@ -36,7 +51,15 @@ thiserror = "2"
36
51
  # exported) so we don't have to track its semver-incompatible version
37
52
  # bumps separately. The `fs` feature on `zarrs_object_store` enables
38
53
  # `LocalFileSystem` in the underlying object_store.
39
- zarrs = { version = "0.22", features = ["async"] }
54
+ #
55
+ # `zlib` enables zarrs's `numcodecs.zlib` codec — a non-standard but common
56
+ # numcodecs-namespace codec that zarr-python writes (e.g. the GOES-16 arraylake
57
+ # dataset uses `bytes -> numcodecs.shuffle -> numcodecs.zlib`). zarrs documents
58
+ # its impl as byte-compatible with zarr-python's, mirroring how icechunk reads
59
+ # such data by delegating to zarr-python's numcodecs shim. `numcodecs.shuffle`
60
+ # is always compiled by zarrs; the other common compressors (gzip, blosc, zstd,
61
+ # crc32c) come from zarrs's default features, which we keep enabled.
62
+ zarrs = { version = "0.22", features = ["async", "zlib"] }
40
63
  zarrs_storage = { version = "0.4", features = ["async"] }
41
64
  zarrs_object_store = { version = "0.6", features = ["fs", "aws"] }
42
65
  serde_json = "1"
@@ -45,9 +68,15 @@ serde_json = "1"
45
68
  # icechunk `Session` as `AsyncReadableListableStorageTraits` so the walk
46
69
  # code stays polymorphic: the same `open_single` runs against either an
47
70
  # icechunk session or a vanilla object_store-backed store.
48
- icechunk = "=2.0.5"
71
+ icechunk = "=2.1.0"
49
72
  zarrs_icechunk = "0.5"
50
73
 
74
+ # msgpack codec — the same one icechunk's `Session::{as,from}_bytes` use. We use
75
+ # it to serialise the `ZarrsArrayHandle` pickle state (`ReopenSpec` + array path)
76
+ # for `dask.distributed` (issue #44), and `src/py_credentials.rs` tests reuse it
77
+ # to round-trip `S3Credentials`.
78
+ rmp-serde = "1"
79
+
51
80
  [lints.clippy]
52
81
  pedantic = { level = "warn", priority = -1 }
53
82
  perf = { level = "deny", priority = -1 }