atlas-python 0.9.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 (76) hide show
  1. atlas_python-0.9.0/.github/workflows/atlas-python-release.yaml +115 -0
  2. atlas_python-0.9.0/.github/workflows/atlas-rust-release.yaml +53 -0
  3. atlas_python-0.9.0/.github/workflows/ci.yaml +51 -0
  4. atlas_python-0.9.0/.gitignore +31 -0
  5. atlas_python-0.9.0/CONTRIBUTING.md +218 -0
  6. atlas_python-0.9.0/Cargo.lock +2875 -0
  7. atlas_python-0.9.0/Cargo.toml +44 -0
  8. atlas_python-0.9.0/LICENSE +201 -0
  9. atlas_python-0.9.0/PKG-INFO +17 -0
  10. atlas_python-0.9.0/README.md +363 -0
  11. atlas_python-0.9.0/atlas-python/.python-version +1 -0
  12. atlas_python-0.9.0/atlas-python/Cargo.toml +20 -0
  13. atlas_python-0.9.0/atlas-python/README.md +275 -0
  14. atlas_python-0.9.0/atlas-python/benchmarks/README.md +249 -0
  15. atlas_python-0.9.0/atlas-python/benchmarks/_common.py +258 -0
  16. atlas_python-0.9.0/atlas-python/benchmarks/bench_collection.py +757 -0
  17. atlas_python-0.9.0/atlas-python/benchmarks/generate_bench_charts.py +141 -0
  18. atlas_python-0.9.0/atlas-python/docs/assets/bench_gridded.svg +1622 -0
  19. atlas_python-0.9.0/atlas-python/docs/assets/bench_profile.svg +1570 -0
  20. atlas_python-0.9.0/atlas-python/docs/benchmarks.md +187 -0
  21. atlas_python-0.9.0/atlas-python/docs/examples.md +36 -0
  22. atlas_python-0.9.0/atlas-python/docs/guides/attributes.md +103 -0
  23. atlas_python-0.9.0/atlas-python/docs/guides/bulk-reads.md +107 -0
  24. atlas_python-0.9.0/atlas-python/docs/guides/cloud-storage.md +151 -0
  25. atlas_python-0.9.0/atlas-python/docs/guides/codecs-and-meta.md +95 -0
  26. atlas_python-0.9.0/atlas-python/docs/guides/dask.md +100 -0
  27. atlas_python-0.9.0/atlas-python/docs/guides/datasets-and-arrays.md +144 -0
  28. atlas_python-0.9.0/atlas-python/docs/guides/dtypes.md +91 -0
  29. atlas_python-0.9.0/atlas-python/docs/guides/durability.md +98 -0
  30. atlas_python-0.9.0/atlas-python/docs/guides/shared-arrays.md +87 -0
  31. atlas_python-0.9.0/atlas-python/docs/guides/stats.md +87 -0
  32. atlas_python-0.9.0/atlas-python/docs/guides/xarray.md +123 -0
  33. atlas_python-0.9.0/atlas-python/docs/index.md +82 -0
  34. atlas_python-0.9.0/atlas-python/docs/installation.md +91 -0
  35. atlas_python-0.9.0/atlas-python/docs/quickstart.md +64 -0
  36. atlas_python-0.9.0/atlas-python/docs/reference/atlas.md +10 -0
  37. atlas_python-0.9.0/atlas-python/docs/reference/dataset-view.md +10 -0
  38. atlas_python-0.9.0/atlas-python/docs/reference/xarray-accessor.md +42 -0
  39. atlas_python-0.9.0/atlas-python/docs/vs-zarr-netcdf.md +139 -0
  40. atlas_python-0.9.0/atlas-python/examples/01_basics.py +67 -0
  41. atlas_python-0.9.0/atlas-python/examples/02_xarray.py +75 -0
  42. atlas_python-0.9.0/atlas-python/examples/03_dask_streaming.py +57 -0
  43. atlas_python-0.9.0/atlas-python/examples/04_meta_formats.py +83 -0
  44. atlas_python-0.9.0/atlas-python/examples/05_codecs.py +91 -0
  45. atlas_python-0.9.0/atlas-python/examples/06_stats_scan.py +77 -0
  46. atlas_python-0.9.0/atlas-python/examples/07_shared_arrays.py +82 -0
  47. atlas_python-0.9.0/atlas-python/examples/08_object_store.py +142 -0
  48. atlas_python-0.9.0/atlas-python/mkdocs.yml +90 -0
  49. atlas_python-0.9.0/atlas-python/src/attr.rs +60 -0
  50. atlas_python-0.9.0/atlas-python/src/dataset.rs +603 -0
  51. atlas_python-0.9.0/atlas-python/src/dtype.rs +101 -0
  52. atlas_python-0.9.0/atlas-python/src/error.rs +32 -0
  53. atlas_python-0.9.0/atlas-python/src/lib.rs +23 -0
  54. atlas_python-0.9.0/atlas-python/src/logging.rs +70 -0
  55. atlas_python-0.9.0/atlas-python/src/runtime.rs +12 -0
  56. atlas_python-0.9.0/atlas-python/src/store.rs +440 -0
  57. atlas_python-0.9.0/atlas-python/tests/GL_PR_BO_JLKU.nc +0 -0
  58. atlas_python-0.9.0/atlas-python/tests/test_smoke.py +556 -0
  59. atlas_python-0.9.0/atlas-python/tests/test_xarray.py +450 -0
  60. atlas_python-0.9.0/examples/lifecycle.rs +213 -0
  61. atlas_python-0.9.0/examples/sensor_fleet.rs +154 -0
  62. atlas_python-0.9.0/examples/weather_store.rs +340 -0
  63. atlas_python-0.9.0/pyproject.toml +40 -0
  64. atlas_python-0.9.0/python/atlas/__init__.py +4 -0
  65. atlas_python-0.9.0/python/atlas/__init__.pyi +332 -0
  66. atlas_python-0.9.0/python/atlas/py.typed +0 -0
  67. atlas_python-0.9.0/python/atlas/xarray.py +578 -0
  68. atlas_python-0.9.0/src/array.rs +115 -0
  69. atlas_python-0.9.0/src/config.rs +94 -0
  70. atlas_python-0.9.0/src/dataset.rs +797 -0
  71. atlas_python-0.9.0/src/error.rs +58 -0
  72. atlas_python-0.9.0/src/lib.rs +184 -0
  73. atlas_python-0.9.0/src/meta.rs +398 -0
  74. atlas_python-0.9.0/src/schema.rs +164 -0
  75. atlas_python-0.9.0/src/store.rs +851 -0
  76. atlas_python-0.9.0/tests/integration.rs +414 -0
@@ -0,0 +1,115 @@
1
+ name: atlas-python release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ release:
6
+ types: [published]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ linux:
13
+ runs-on: ${{ matrix.runner }}
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ include:
18
+ - target: x86_64
19
+ runner: ubuntu-latest
20
+ - target: aarch64
21
+ runner: ubuntu-24.04-arm
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: actions/setup-python@v6
25
+ with:
26
+ python-version: "3.10"
27
+ - name: Build wheels
28
+ uses: PyO3/maturin-action@v1
29
+ with:
30
+ target: ${{ matrix.target }}
31
+ manylinux: auto
32
+ sccache: "true"
33
+ args: --release --out dist --manifest-path atlas-python/Cargo.toml
34
+ - uses: actions/upload-artifact@v4
35
+ with:
36
+ name: wheels-linux-${{ matrix.target }}
37
+ path: dist
38
+
39
+ macos:
40
+ runs-on: macos-latest
41
+ strategy:
42
+ fail-fast: false
43
+ matrix:
44
+ target: [x86_64, aarch64]
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+ - uses: actions/setup-python@v6
48
+ with:
49
+ python-version: "3.10"
50
+ - name: Build wheels
51
+ uses: PyO3/maturin-action@v1
52
+ with:
53
+ target: ${{ matrix.target }}
54
+ sccache: "true"
55
+ args: --release --out dist --manifest-path atlas-python/Cargo.toml
56
+ - uses: actions/upload-artifact@v4
57
+ with:
58
+ name: wheels-macos-${{ matrix.target }}
59
+ path: dist
60
+
61
+ windows:
62
+ runs-on: windows-latest
63
+ strategy:
64
+ fail-fast: false
65
+ matrix:
66
+ target: [x64]
67
+ steps:
68
+ - uses: actions/checkout@v4
69
+ - uses: actions/setup-python@v6
70
+ with:
71
+ python-version: "3.10"
72
+ architecture: ${{ matrix.target }}
73
+ - name: Build wheels
74
+ uses: PyO3/maturin-action@v1
75
+ with:
76
+ target: ${{ matrix.target }}
77
+ sccache: "true"
78
+ args: --release --out dist --manifest-path atlas-python/Cargo.toml
79
+ - uses: actions/upload-artifact@v4
80
+ with:
81
+ name: wheels-windows-${{ matrix.target }}
82
+ path: dist
83
+
84
+ sdist:
85
+ runs-on: ubuntu-latest
86
+ steps:
87
+ - uses: actions/checkout@v4
88
+ - name: Build sdist
89
+ uses: PyO3/maturin-action@v1
90
+ with:
91
+ command: sdist
92
+ args: --out dist --manifest-path atlas-python/Cargo.toml
93
+ - uses: actions/upload-artifact@v4
94
+ with:
95
+ name: wheels-sdist
96
+ path: dist
97
+
98
+ release:
99
+ name: Publish to PyPI
100
+ runs-on: ubuntu-latest
101
+ if: github.event_name == 'release'
102
+ needs: [linux, macos, windows, sdist]
103
+ permissions:
104
+ id-token: write
105
+ environment:
106
+ name: pypi
107
+ url: https://pypi.org/p/atlas-python
108
+ steps:
109
+ - uses: actions/download-artifact@v4
110
+ with:
111
+ pattern: wheels-*
112
+ path: dist
113
+ merge-multiple: true
114
+ - name: Publish to PyPI
115
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,53 @@
1
+ name: atlas-rust release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ release:
6
+ types: [published]
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ name: Test
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: dtolnay/rust-toolchain@stable
18
+ - uses: Swatinem/rust-cache@v2
19
+ - name: Build workspace
20
+ run: cargo build --workspace --verbose
21
+ - name: Test atlas crate
22
+ # atlas-python links `pyo3/extension-module` (no libpython), so a
23
+ # plain `cargo test` binary can't link there — test the core crate.
24
+ run: cargo test -p atlas-rust --verbose
25
+
26
+ publish:
27
+ name: Publish to crates.io
28
+ runs-on: ubuntu-latest
29
+ needs: test
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - uses: dtolnay/rust-toolchain@stable
33
+ - uses: Swatinem/rust-cache@v2
34
+
35
+ - name: Verify tag matches crate version
36
+ if: github.event_name == 'release'
37
+ run: |
38
+ crate_version="$(cargo metadata --no-deps --format-version 1 \
39
+ | python3 -c "import json,sys; print(next(p['version'] for p in json.load(sys.stdin)['packages'] if p['name']=='atlas-rust'))")"
40
+ tag_version="${GITHUB_REF_NAME#v}"
41
+ echo "crate=$crate_version tag=$tag_version"
42
+ if [ "$crate_version" != "$tag_version" ]; then
43
+ echo "::error::Tag $GITHUB_REF_NAME does not match crate version $crate_version"
44
+ exit 1
45
+ fi
46
+
47
+ - name: Package (dry run)
48
+ run: cargo publish -p atlas-rust --locked --dry-run
49
+
50
+ - name: Publish
51
+ env:
52
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
53
+ run: cargo publish -p atlas-rust --locked
@@ -0,0 +1,51 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ rust:
18
+ name: Rust tests
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: dtolnay/rust-toolchain@stable
23
+ - uses: Swatinem/rust-cache@v2
24
+ - name: Build workspace
25
+ run: cargo build --workspace --verbose
26
+ - name: Test atlas crate
27
+ # atlas-python links `pyo3/extension-module` (no libpython), so a
28
+ # plain `cargo test` binary can't link there — test the core crate.
29
+ run: cargo test -p atlas-rust --verbose
30
+
31
+ python:
32
+ name: Python tests
33
+ runs-on: ubuntu-latest
34
+ strategy:
35
+ fail-fast: false
36
+ matrix:
37
+ python-version: ["3.10", "3.13"]
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ - uses: actions/setup-python@v6
41
+ with:
42
+ python-version: ${{ matrix.python-version }}
43
+ - uses: dtolnay/rust-toolchain@stable
44
+ - uses: Swatinem/rust-cache@v2
45
+ - name: Build extension and run pytest
46
+ run: |
47
+ python -m venv .venv
48
+ source .venv/bin/activate
49
+ python -m pip install --upgrade pip maturin
50
+ maturin develop --extras test --manifest-path atlas-python/Cargo.toml
51
+ pytest atlas-python/tests -v
@@ -0,0 +1,31 @@
1
+ # Generated by Cargo
2
+ # will have compiled files and executables
3
+ debug
4
+ target
5
+
6
+ # These are backup files generated by rustfmt
7
+ **/*.rs.bk
8
+
9
+ # MSVC Windows builds of rustc generate these, which store debugging information
10
+ *.pdb
11
+
12
+ # Generated by cargo mutants
13
+ # Contains mutation testing data
14
+ **/mutants.out*/
15
+
16
+ # RustRover
17
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
20
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21
+ #.idea/
22
+ .venv/
23
+ __pycache__/
24
+ .pytest_cache/
25
+ raw_smoke_tests/
26
+ .DS_Store
27
+ .claude/
28
+ pyatlas/python/pyatlas/_pyatlas.pyd
29
+ pyatlas/python/pyatlas/_pyatlas.abi3.so
30
+ playground/
31
+ data/
@@ -0,0 +1,218 @@
1
+ # Contributing to ATLAS
2
+
3
+ Thanks for your interest. This document walks through what the project is, how the pieces fit, and how to set up a development environment.
4
+
5
+ For end-user docs see the top-level [README.md](README.md) (Rust crate) and [pyatlas/README.md](pyatlas/README.md) (Python bindings).
6
+
7
+ ---
8
+
9
+ ## What this is
10
+
11
+ **ATLAS** (Aggregated Tensor Large Array Store) is a directory-based store for many similarly-shaped, named, N-dimensional arrays. The design goal is fast cross-dataset variable scans — opening a single file to read variable `X` across N datasets, rather than N files.
12
+
13
+ The repository is a Cargo workspace containing two crates:
14
+
15
+ | Crate | Purpose |
16
+ | --- | --- |
17
+ | `atlas` (workspace root) | Rust library implementing the store and on-disk format. |
18
+ | `pyatlas` ([pyatlas/](pyatlas/)) | PyO3 Python bindings + xarray integration. |
19
+
20
+ ---
21
+
22
+ ## How it works (architecture)
23
+
24
+ ### On-disk layout
25
+
26
+ ```
27
+ my_store/
28
+ ├── atlas.json ← dataset registry + per-dataset attributes (JSON)
29
+ ├── temperature/
30
+ │ └── data.af ← ArrayFile: every dataset's "temperature" in one file
31
+ ├── pressure/
32
+ │ └── data.af
33
+ └── time/
34
+ └── data.af
35
+ ```
36
+
37
+ Every variable name owns one physical `.af` file shared by all datasets that define it (variable-first). The `.af` format comes from the [`array-format`](https://github.com/robinskil/array-format) crate — it's a columnar, chunk-oriented binary container with per-block compression and persisted statistics.
38
+
39
+ `atlas.json` is the catalog. It records:
40
+ - Store version, store-level codec.
41
+ - Each dataset: its array schemas (dtype, shape, chunk shape, dimension names) and typed attributes.
42
+
43
+ ### Rust crate (`atlas`)
44
+
45
+ | File | Role |
46
+ | --- | --- |
47
+ | [src/lib.rs](src/lib.rs) | Public re-exports; `validate_name`; thread-safety asserts. |
48
+ | [src/store.rs](src/store.rs) | The `Atlas` struct: open/create, dataset CRUD, path-based wrappers (`open_path` / `create_path`). |
49
+ | [src/dataset.rs](src/dataset.rs) | The `DatasetView` struct: define/read/write arrays, attributes, flush, compact. Also the shared `ArrayFile` cache. |
50
+ | [src/meta.rs](src/meta.rs) | `atlas.json` (de)serialisation. |
51
+ | [src/schema.rs](src/schema.rs) | `ArraySchema` and the typed `Attr` enum. |
52
+ | [src/config.rs](src/config.rs) | `StoreConfig`, `Codec` (`Zstd` / `Lz4` / `Uncompressed`). |
53
+ | [src/error.rs](src/error.rs) | `Error` / `Result`. |
54
+
55
+ The API is async (tokio). Each physical array file is guarded by a `tokio::sync::RwLock` — reads share, writes are exclusive. The cache map uses `parking_lot::RwLock` and is never held across an `await`.
56
+
57
+ ### Python bindings (`pyatlas`)
58
+
59
+ Mixed Python/Rust maturin layout:
60
+
61
+ ```
62
+ pyatlas/
63
+ ├── Cargo.toml ← cdylib named `_pyatlas`
64
+ ├── pyproject.toml ← maturin build backend
65
+ ├── python/pyatlas/
66
+ │ ├── __init__.py ← re-exports + xarray accessor registration
67
+ │ ├── __init__.pyi ← type stubs (PEP 561)
68
+ │ ├── py.typed ← marker
69
+ │ └── xarray.py ← xarray integration + ds.atlas accessor
70
+ ├── src/
71
+ │ ├── lib.rs ← #[pymodule] _pyatlas wiring
72
+ │ ├── runtime.rs ← shared OnceLock<tokio::Runtime>
73
+ │ ├── error.rs ← atlas::Error → PyErr mapping
74
+ │ ├── dtype.rs ← dtype string parsing & DType ↔ name
75
+ │ ├── attr.rs ← Attr ↔ Python primitive conversion
76
+ │ ├── store.rs ← PyAtlas (wraps atlas::Atlas)
77
+ │ └── dataset.rs ← PyDatasetView (wraps atlas::DatasetView)
78
+ ├── tests/ ← pytest tests
79
+ └── examples/ ← runnable example scripts
80
+ ```
81
+
82
+ Key design points:
83
+ - **Sync Python API, backed by an internal multi-threaded tokio runtime.** Each blocking call uses `py.allow_threads(|| RT.block_on(...))` so other Python threads can run.
84
+ - **Type dispatch via macros.** Atlas's read/write methods are generic over dtype (`T: ArrayElement`); the bindings dispatch at runtime via a `numeric_dispatch!` macro in [pyatlas/src/dataset.rs](pyatlas/src/dataset.rs).
85
+ - **`numpy`-zero-copy on the numeric path.** Python `np.ndarray` ↔ `ndarray::ArrayView` via the `numpy` crate.
86
+ - **xarray integration** lives in [pyatlas/python/pyatlas/xarray.py](pyatlas/python/pyatlas/xarray.py). The `Atlas.add_xr_dataset` Rust pymethod and the `ds.atlas.write` accessor both delegate to the `_write_xarray_new_dataset` helper. dask-backed variables are streamed one chunk at a time via `arr.blocks[idx].compute()`.
87
+
88
+ ---
89
+
90
+ ## Prerequisites
91
+
92
+ | Tool | Why |
93
+ | --- | --- |
94
+ | **Rust** (stable, 1.85+ for edition 2024 in the atlas crate) | Build the workspace. |
95
+ | **Python ≥ 3.9** | Run / build pyatlas (wheel targets `abi3-py39`). |
96
+ | **`maturin`** | Build the Python extension. `pip install maturin`. |
97
+
98
+ Install Rust via [rustup.rs](https://rustup.rs). Python 3.13 is what the maintainers develop against.
99
+
100
+ ---
101
+
102
+ ## Building & testing the Rust crate
103
+
104
+ ```bash
105
+ cargo build # compile the atlas crate + workspace
106
+ cargo test # run all tests (60 in atlas, plus pyatlas check-only)
107
+ cargo test -p atlas # just the atlas crate (50 unit + 10 integration)
108
+ cargo run --example lifecycle # try one of the Rust examples
109
+ ```
110
+
111
+ Examples live in [examples/](examples/) — `lifecycle.rs`, `sensor_fleet.rs`, `weather_store.rs`.
112
+
113
+ ---
114
+
115
+ ## Building & installing the Python library
116
+
117
+ The Python extension links against libpython, so plain `cargo build -p pyatlas` will fail at link time. Use `maturin develop` (which sets the right flags and installs into the active virtualenv).
118
+
119
+ ### One-time setup
120
+
121
+ ```bash
122
+ # From the repo root
123
+ python3.13 -m venv .venv
124
+ source .venv/bin/activate
125
+ pip install --upgrade pip maturin
126
+ ```
127
+
128
+ `pyatlas` already lists `numpy`, `xarray`, and `dask` as required runtime deps, so `maturin develop` will pull them in automatically.
129
+
130
+ ### Development build
131
+
132
+ ```bash
133
+ cd pyatlas
134
+ maturin develop --release # builds, then editable-installs into the active venv
135
+ ```
136
+
137
+ After `maturin develop`, the package is editable: changes to `python/pyatlas/*.py` take effect immediately, but Rust changes require re-running `maturin develop`.
138
+
139
+ ### Building a distributable wheel
140
+
141
+ ```bash
142
+ cd pyatlas
143
+ maturin build --release # produces pyatlas-0.1.0-*.whl in target/wheels/
144
+ ```
145
+
146
+ ### Verification
147
+
148
+ ```bash
149
+ .venv/bin/python -c "import pyatlas; print(pyatlas.__version__)"
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Running the Python tests
155
+
156
+ ```bash
157
+ .venv/bin/pip install pytest # one-time
158
+ .venv/bin/pytest pyatlas/tests/ -v
159
+ ```
160
+
161
+ The two test files:
162
+ - [pyatlas/tests/test_smoke.py](pyatlas/tests/test_smoke.py) — exercises the core pyatlas API.
163
+ - [pyatlas/tests/test_xarray.py](pyatlas/tests/test_xarray.py) — xarray accessor + dask streaming.
164
+
165
+ After every Rust change re-run `maturin develop` first, otherwise pytest will run against the previously-built binary.
166
+
167
+ ---
168
+
169
+ ## Running the Python examples
170
+
171
+ Self-contained scripts under [pyatlas/examples/](pyatlas/examples/) that write to temp directories:
172
+
173
+ ```bash
174
+ .venv/bin/python pyatlas/examples/01_basics.py
175
+ .venv/bin/python pyatlas/examples/02_xarray.py
176
+ .venv/bin/python pyatlas/examples/03_dask_streaming.py
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Common dev workflows
182
+
183
+ ### Adding a new array dtype
184
+
185
+ 1. Add the variant to `atlas::DType` (depends on `array-format` supporting it).
186
+ 2. Add the dispatch arm in [pyatlas/src/dataset.rs](pyatlas/src/dataset.rs)'s `numeric_dispatch!` (or the explicit String/Binary branches).
187
+ 3. Extend [pyatlas/src/dtype.rs](pyatlas/src/dtype.rs) to parse the new dtype name.
188
+ 4. Update [pyatlas/python/pyatlas/xarray.py](pyatlas/python/pyatlas/xarray.py)'s `_NUMPY_TO_ATLAS` map if it has a numpy equivalent.
189
+ 5. Add a smoke test in [pyatlas/tests/test_smoke.py](pyatlas/tests/test_smoke.py).
190
+
191
+ ### Exposing a new `DatasetView` / `Atlas` method to Python
192
+
193
+ 1. Implement on the Rust side in [src/store.rs](src/store.rs) or [src/dataset.rs](src/dataset.rs).
194
+ 2. Wrap as a pymethod in [pyatlas/src/store.rs](pyatlas/src/store.rs) / [pyatlas/src/dataset.rs](pyatlas/src/dataset.rs). Use `py.allow_threads(|| runtime().block_on(...))` for async calls.
195
+ 3. Add the stub in [pyatlas/python/pyatlas/\_\_init\_\_.pyi](pyatlas/python/pyatlas/__init__.pyi).
196
+ 4. `cd pyatlas && maturin develop --release`.
197
+ 5. Write a test.
198
+
199
+ ### Touching the on-disk format
200
+
201
+ Format changes are breaking — bump the `version` field in `StoreMeta` ([src/meta.rs](src/meta.rs)) and load both versions during `load_meta` until a migration path is clear.
202
+
203
+ ---
204
+
205
+ ## Code style
206
+
207
+ - Rust: `cargo fmt` before committing; we follow the default rustfmt configuration. `cargo clippy --all-targets` should be clean.
208
+ - Python: top-level scripts and tests use ordinary 4-space indentation and import ordering (stdlib, third-party, local). No formatter is enforced; match the existing style.
209
+ - Comments and docstrings explain *why*, not what — the code already shows what.
210
+
211
+ ---
212
+
213
+ ## Pull requests
214
+
215
+ 1. Branch from `main`.
216
+ 2. Make sure `cargo test`, `cargo clippy --all-targets`, and `pytest pyatlas/tests/` all pass.
217
+ 3. If you change the on-disk format, the public API, or any behaviour visible to end users, update the relevant README.
218
+ 4. Keep commits focused; squash trivial fixups before review.