zarrcurator 1.0.0b1__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.
@@ -0,0 +1,403 @@
1
+ Metadata-Version: 2.4
2
+ Name: zarrcurator
3
+ Version: 1.0.0b1
4
+ Summary: Analyze Zarr stores for completeness, sizing, and archival distribution workflows
5
+ Keywords: zarr,analysis,data-archiving,cloud-storage,scientific-data
6
+ Author: Kameswarrao Modali
7
+ Author-email: Kameswarrao Modali <modali@dkrz.de>
8
+ License-Expression: GPL-3.0-only
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Classifier: Topic :: System :: Archiving
16
+ Requires-Dist: cftime>=1.6.6
17
+ Requires-Dist: fastparquet>=2026.9.0
18
+ Requires-Dist: fsspec>=2026.2.0
19
+ Requires-Dist: kerchunk>=0.2.10
20
+ Requires-Dist: matplotlib>=3.10.8
21
+ Requires-Dist: plotly>=6.6.0
22
+ Requires-Dist: pyslk>=2.3.0
23
+ Requires-Dist: rich>=14.3.3
24
+ Requires-Dist: s3fs>=2026.9.0
25
+ Requires-Dist: tqdm>=4.70.1
26
+ Requires-Dist: ujson>=6.0.0
27
+ Requires-Dist: validators>=0.35.0
28
+ Requires-Dist: xarray>=2026.7.0
29
+ Requires-Dist: zarr>=3.4.1
30
+ Requires-Dist: build>=1.4.0 ; extra == 'dev'
31
+ Requires-Dist: codespell>=2.4.1 ; extra == 'dev'
32
+ Requires-Dist: mypy>=1.19.1 ; extra == 'dev'
33
+ Requires-Dist: pre-commit>=4.3.0 ; extra == 'dev'
34
+ Requires-Dist: pytest>=9.0.2 ; extra == 'dev'
35
+ Requires-Dist: ruff>=0.15.4 ; extra == 'dev'
36
+ Requires-Dist: twine>=6.2.0 ; extra == 'dev'
37
+ Requires-Dist: sphinx>=9.1.0 ; extra == 'docs'
38
+ Requires-Dist: sphinx-rtd-theme>=3.1.0 ; extra == 'docs'
39
+ Requires-Dist: myst-parser>=5.1.0 ; extra == 'docs'
40
+ Requires-Dist: rst2pdf>=0.105 ; extra == 'docs'
41
+ Requires-Python: >=3.12
42
+ Project-URL: Homepage, https://github.com/kmodali/zarrcurator
43
+ Project-URL: Issues, https://github.com/kmodali/zarrcurator/issues
44
+ Project-URL: Documentation, https://zarrcurator.readthedocs.io
45
+ Project-URL: Changelog, https://github.com/kmodali/zarrcurator/blob/main/CHANGELOG.md
46
+ Provides-Extra: dev
47
+ Provides-Extra: docs
48
+ Description-Content-Type: text/markdown
49
+
50
+ # zarrcurator
51
+
52
+ zarrcurator analyzes Zarr stores for completeness and sizing, supports visualization, and can package selected content into tar archives for downstream archival workflows.
53
+
54
+ ## Repository Layout
55
+
56
+ - `src/zarrcurator`: package source code
57
+ - `tests`: repository-level test suite
58
+
59
+ ## Contributing
60
+
61
+ Contribution workflow, coding rules, testing expectations, and PR guidelines are documented in `CONTRIBUTING.md`.
62
+
63
+ ## Installation
64
+
65
+ zarrcurator requires Python 3.12 or newer and zarr 3.4.1 or newer (the version in `uv.lock`).
66
+ Older zarr versions read a zero-size shard as not written, so the check did not report it as
67
+ corrupt.
68
+
69
+ Install from a local clone (recommended for development):
70
+
71
+ ```bash
72
+ cd /path/to/zarrcurator
73
+ python -m pip install -e .
74
+ ```
75
+
76
+ Install as a regular package from a local clone:
77
+
78
+ ```bash
79
+ cd /path/to/zarrcurator
80
+ python -m pip install .
81
+ ```
82
+
83
+ Build wheel/sdist and install the wheel:
84
+
85
+ ```bash
86
+ cd /path/to/zarrcurator
87
+ python -m pip install build
88
+ python -m build
89
+ python -m pip install dist/*.whl
90
+ ```
91
+
92
+ Optional: install development tooling used in this repository:
93
+
94
+ ```bash
95
+ cd /path/to/zarrcurator
96
+ python -m pip install -e .[dev]
97
+ ```
98
+
99
+ Optional: install documentation tooling:
100
+
101
+ ```bash
102
+ cd /path/to/zarrcurator
103
+ python -m pip install -e .[docs]
104
+ ```
105
+
106
+ ## Development Commands
107
+
108
+ Run unit test suite:
109
+
110
+ ```bash
111
+ python -m pytest
112
+ python -m tests.test_all_modules
113
+ python -m tests.regression_test
114
+ ```
115
+
116
+ `python -m pytest` ends with a summary table of the results per test class (tests and
117
+ subtests passed, failed and skipped, and what each class covers, from its docstring).
118
+ `--summary-table-file=summary.md` also writes it to a file, `--no-summary-table` turns it off.
119
+
120
+ Run the store layout tests (zarr v2 and v3, `.` and `/` separators, nested groups, sharding):
121
+
122
+ ```bash
123
+ python -m pytest tests/test_all_modules.py -k TestChunkKeySeparators
124
+ python -m pytest tests/test_all_modules.py -k TestDataTreeStores
125
+ python -m pytest tests/test_all_modules.py -k TestZarrV3Metadata
126
+ python -m pytest tests/test_all_modules.py -k TestSyntheticStoreVariants
127
+ python -m pytest tests/regression_test.py -k synthetic_store_layouts
128
+ ```
129
+
130
+ Run focused config/object-store tests:
131
+
132
+ ```bash
133
+ python -m unittest tests.test_all_modules.TestConfigAndSubgroup
134
+ ```
135
+
136
+ Generate API documentation from source docstrings:
137
+
138
+ ```bash
139
+ python scripts/generate_api_docstrings.py
140
+ ```
141
+
142
+ Build Read the Docs style documentation (HTML and PDF):
143
+
144
+ ```bash
145
+ python -m sphinx -b html docs docs/_build/html
146
+ python -m sphinx -b pdf docs docs/_build/pdf
147
+ ```
148
+
149
+ ## Releases
150
+
151
+ Repository release process and checklist are documented in `RELEASE.md`.
152
+
153
+ Bump package version (single-source in `pyproject.toml`):
154
+
155
+ ```bash
156
+ python scripts/release.py --bump patch
157
+ ```
158
+
159
+ Automated publishing is configured via GitHub Actions Trusted Publishing:
160
+
161
+ - `.github/workflows/publish-pypi.yml`
162
+ - tag push `v*` publishes to PyPI
163
+ - manual `workflow_dispatch` supports TestPyPI and PyPI targets
164
+
165
+ Version consistency CI is configured via:
166
+
167
+ - `.github/workflows/version-consistency.yml`
168
+
169
+ Run pre-commit quality checks (lint, type checks, spell checks, and hygiene checks):
170
+
171
+ ```bash
172
+ python -m pip install pre-commit
173
+ pre-commit install
174
+ pre-commit run --all-files
175
+ ```
176
+
177
+ Run benchmark harness:
178
+
179
+ ```bash
180
+ python -m zarrcurator.benchmark --help
181
+ python -m zarrcurator.benchmark --synthetic --phase diagnose
182
+ python -m zarrcurator.benchmark --synthetic --phase both
183
+ ```
184
+
185
+ Create a synthetic Zarr experiment to test diagnose/distribute/dispatch flows:
186
+
187
+ ```bash
188
+ cd src
189
+ python -m zarrcurator.synthetic_store --output-dir ../tests/data/synthetic_experiment --clean --zarr-formats 2,3 --run-smoke
190
+ ```
191
+
192
+ By default each format uses its own chunk key separator (v2 `0.1`, v3 `c/0/1`), no
193
+ sharding and a flat layout (all arrays in the root group). These options add variants:
194
+
195
+ - `--separators .,/`: both chunk key separators for each format.
196
+ - `--shards 4,4,8`: the zarr v3 stores also with sharded data variables (shard shape of
197
+ `time,lat,lon`, a multiple of the chunk shape `2,4,4`).
198
+ - `--layouts flat,tree`: also stores with nested groups, as written from an xarray DataTree:
199
+ `time` in the root group, `lat`, `lon`, `temp` and `humidity` in `/atmos`, and `salinity`
200
+ in `/atmos/ocean`, which uses the coordinates of the groups above it.
201
+
202
+ All variants, as in the committed fixture:
203
+
204
+ ```bash
205
+ python -m zarrcurator.synthetic_store --output-dir tests/data/synthetic_experiment --clean \
206
+ --zarr-formats 2,3 --separators .,/ --shards 4,4,8 --layouts flat,tree --stores-per-frequency 1
207
+ ```
208
+
209
+ The variant is tagged in the store name, only where it differs from the default:
210
+
211
+ | Format | Separator | Chunk key | Sharded | Layout | Store name |
212
+ | ------ | --------- | --------- | ------- | ------ | ---------- |
213
+ | v2 | `.` | `0.0.0` | no | flat | `syntheticexp_P1D_000_z2.zarr` |
214
+ | v2 | `/` | `0/0/0` | no | flat | `syntheticexp_P1D_000_z2slash.zarr` |
215
+ | v3 | `/` | `c/0/0/0` | no | flat | `syntheticexp_P1D_000_z3.zarr` |
216
+ | v3 | `.` | `c.0.0.0` | no | flat | `syntheticexp_P1D_000_z3dot.zarr` |
217
+ | v3 | `/` | `c/0/0/0` (shard) | yes | flat | `syntheticexp_P1D_000_z3shard.zarr` |
218
+ | v2 | `.` | `0.0.0` | no | tree | `syntheticexp_P1D_000_z2tree.zarr` |
219
+ | v3 | `.` | `c.0.0.0` (shard) | yes | tree | `syntheticexp_P1D_000_z3dotshardtree.zarr` |
220
+
221
+ All arrays carry dimension names, so the stores open with xarray. The generated
222
+ `manifest.json` records each store's `zarr_format`, `separator`, `shards`, `layout`, the
223
+ array the anomalies are injected into (`anomaly_array`: `temp`, or `atmos/ocean/salinity`
224
+ in a tree) and the anomalies (`zero_size`, `missing`, `corrupt`, `unexpected`). All paths in
225
+ it are relative to the manifest's directory, so the fixture works from any checkout.
226
+
227
+ ## Store Layouts
228
+
229
+ zarr v2 and v3 stores with `.` or `/` chunk key separators, nested groups (e.g. written with
230
+ `xarray.DataTree.to_zarr`) and sharded arrays are supported, from diagnosis to the Kerchunk
231
+ references of the tarballs. A store must open with xarray (all arrays need dimension names);
232
+ `dispatchStoreToTarStores` checks this first and raises a `ValueError` otherwise. The Kerchunk
233
+ references of all tarballs of a store are merged into one parquet reference of the store, in
234
+ the zarr v2 layout; stores with nested groups are opened from it with `consolidated=True`.
235
+ Details and examples: [docs/store_layouts.md](docs/store_layouts.md).
236
+
237
+ ## Large Stores and Several Sessions
238
+
239
+ The steps that read a whole store can be interrupted and continued in a later session, by
240
+ calling them again in the same working directory:
241
+
242
+ | Step | Kept in the working directory | Continued by |
243
+ |---|---|---|
244
+ | `diagnoseStore` | `<store>.diag.checkpoint`: the entries diagnosed so far (every 30 s) | diagnosing only the remaining entries; the `.diag` equals that of a single session, and the checkpoint is removed when the `.diag` is written |
245
+ | `findMissingChunksInStore` | `<store>.check`: rewritten after each array; with `checkpointSignal`, `<store>.check.partial`: the chunks checked so far in unfinished arrays, written when that signal arrives | skipping the arrays it lists (with unchanged shape, chunks and shards) and the chunks checked in partly checked arrays; the partial file is removed when all arrays are checked |
246
+ | `dispatchStoreToTarStores` | the tarballs; each is written as `<name>.tar.part` and renamed when complete | skipping the tarballs that exist completely (per bin for arrays split into chunk tarballs) |
247
+
248
+ To start a step from the beginning, delete its checkpoint, report or tarballs, or set
249
+ `Dispatcher.skipExistingTars = False`. Details, and recommended settings for running a large
250
+ check as a SLURM job on one whole node: [docs/overview.md](docs/overview.md).
251
+
252
+ ## Known Issues and Limitations
253
+
254
+ See [docs/known_issues.md](docs/known_issues.md). In short:
255
+
256
+ - Target sizes below 1 GiB fail for arrays larger than the target size.
257
+ - A metadata file larger than the target size (e.g. the consolidated metadata of a large
258
+ store with many groups) fails.
259
+ - Kerchunk references are zarr v2: zarr v3 codecs without a zarr v2 equivalent are rejected,
260
+ and a single shard larger than the target size makes an oversized tarball.
261
+
262
+ ## Object Store Access
263
+
264
+ For supported S3 endpoints, zarrcurator now uses this access order:
265
+
266
+ 1. Try anonymous access first.
267
+ 2. If anonymous access fails, try configured credentials.
268
+ 3. If credentials are missing and anonymous access fails, exit with a clear error message.
269
+
270
+ You can provide credentials in three supported ways:
271
+
272
+ 1. Use the new utility helper (recommended for notebooks/scripts).
273
+ 2. Use config setters with environment-variable names.
274
+ 3. Export environment variables directly.
275
+
276
+ Using the utility helper:
277
+
278
+ ```python
279
+ import zarrcurator
280
+
281
+ # Accepts raw values OR env-var names containing the values.
282
+ zarrcurator.setObjectStoreCredentials(
283
+ accessKey="AWS_ACCESS_KEY_ID",
284
+ secretKey="AWS_SECRET_ACCESS_KEY",
285
+ )
286
+ ```
287
+
288
+ You can also pass credentials directly on store-facing utility calls:
289
+
290
+ ```python
291
+ from zarrcurator.utils import diagnoseStore, findMissingChunksInStore
292
+
293
+ diagnoseStore(
294
+ "https://s3.eu-dkrz-3.dkrz.cloud/mybucket/my-store.zarr",
295
+ accessKey="AWS_ACCESS_KEY_ID",
296
+ secretKey="AWS_SECRET_ACCESS_KEY",
297
+ )
298
+
299
+ findMissingChunksInStore(
300
+ "https://s3.eu-dkrz-3.dkrz.cloud/mybucket/my-store.zarr",
301
+ accessKey="AKIA...",
302
+ secretKey="...",
303
+ )
304
+ ```
305
+
306
+ Using config setters:
307
+
308
+ ```python
309
+ from zarrcurator.config import configObj
310
+
311
+ # Pass environment variable names.
312
+ configObj.setaccessKey("AWS_ACCESS_KEY_ID")
313
+ configObj.setsecretKey("AWS_SECRET_ACCESS_KEY")
314
+ ```
315
+
316
+ Using environment variables:
317
+
318
+ ```bash
319
+ export AWS_ACCESS_KEY_ID="<ACCESS_KEY>"
320
+ export AWS_SECRET_ACCESS_KEY="<SECRET_KEY>"
321
+ ```
322
+
323
+ Alternative variable names are also recognized:
324
+
325
+ - `S3_ACCESS_KEY_ID`
326
+ - `S3_SECRET_ACCESS_KEY`
327
+
328
+ ## Multithreading
329
+
330
+ Store diagnosis and missing-chunk detection run single-threaded by default. To enable multithreading, set:
331
+
332
+ ```bash
333
+ export ZARRCURATOR_MULTITHREAD=1
334
+ ```
335
+
336
+ When enabled, diagnosis (`diagnose`, `diagnoseStore`):
337
+
338
+ - diagnoses the Zarr stores of an experiment in parallel (up to 10 threads)
339
+ - diagnoses the variables within each store in parallel (up to 10 threads)
340
+ - lists all files of each variable, including chunks in sub directories (`/` separator), and
341
+ gathers their sizes in parallel on local stores, with the thread count scaled by the number of
342
+ files (1, 10, 40, or 100 threads for fewer than 200, 200–499, 500–999, or 1000 and more); on
343
+ object stores the listing already holds the sizes
344
+
345
+ When enabled, missing-chunk detection (`findMissingChunksInStore`):
346
+
347
+ - checks several arrays at once (8 by default, at most the number of available CPUs; set
348
+ `ZARRCURATOR_ARRAY_WORKERS` to change it), so that small arrays, e.g. of low zoom levels, do not
349
+ run one after the other
350
+ - queries chunk sizes (zero-size detection) in parallel
351
+ - checks chunk readability (corruption detection) in parallel
352
+
353
+ The size queries and readability checks of all arrays share two thread pools; each array keeps
354
+ at most as many chunks in progress as the number of its chunks gives (1, 10, 40, or 100, see
355
+ `numThreadsForChunks`). The report is written after each array, in the order of the store.
356
+ The log has a `check_array start` and a `check_array end` line per array; the end line gives
357
+ the seconds, the bytes read, the number of chunks and the rate in GB/s, so the speed of a check
358
+ can be measured per array.
359
+
360
+ The thread counts are capped by the CPUs available to the process (within a SLURM job, the CPUs
361
+ allocated to it): I/O-bound work (listing files, querying sizes) uses at most 4 threads per
362
+ CPU, CPU-bound work (reading and decompressing chunks for the corruption check) at most one
363
+ thread per CPU; for `findMissingChunksInStore` these are the sizes of the two shared pools. For
364
+ example, with 8 CPUs at most 32 threads query sizes and 8 check chunks. Set
365
+ `ZARRCURATOR_MAX_THREADS` to a positive integer to set the maximum for all work instead:
366
+
367
+ ```bash
368
+ export ZARRCURATOR_MAX_THREADS=16
369
+ ```
370
+
371
+ Any value other than `1` (or leaving the variable unset) keeps single-threaded mode. The active mode is reported in the logs (`mode=single-thread` / `mode=multi-thread`).
372
+
373
+ From Python, set the variable before running a diagnosis:
374
+
375
+ ```python
376
+ import os
377
+
378
+ os.environ["ZARRCURATOR_MULTITHREAD"] = "1"
379
+ ```
380
+
381
+ ## Logging
382
+
383
+ CLI logging level can be set via:
384
+
385
+ - argument: `--log-level DEBUG`
386
+ - environment variable: `ZARRCURATOR_LOG_LEVEL=DEBUG`
387
+
388
+ During CLI usage, logs are also written to a timestamped file:
389
+
390
+ - default directory: `logs/`
391
+ - filename pattern: `zarrcurator_YYYYMMDD_HHMMSS.log`
392
+
393
+ You can override the log directory with:
394
+
395
+ - environment variable: `ZARRCURATOR_LOG_DIR=/path/to/logs`
396
+
397
+ For notebook/library usage (non-CLI), enable the same logging with:
398
+
399
+ ```python
400
+ import zarrcurator
401
+
402
+ zarrcurator.enable_logging("INFO")
403
+ ```