dexverse-cli 0.1.3.post1__tar.gz → 0.1.4__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 (48) hide show
  1. {dexverse_cli-0.1.3.post1 → dexverse_cli-0.1.4}/.gitignore +12 -12
  2. {dexverse_cli-0.1.3.post1 → dexverse_cli-0.1.4}/.python-version +1 -1
  3. dexverse_cli-0.1.4/PKG-INFO +301 -0
  4. {dexverse_cli-0.1.3.post1 → dexverse_cli-0.1.4}/pyproject.toml +69 -68
  5. dexverse_cli-0.1.4/src/dexverse_cli/README.md +286 -0
  6. dexverse_cli-0.1.4/src/dexverse_cli/__init__.py +1 -0
  7. dexverse_cli-0.1.4/src/dexverse_cli/cli.py +236 -0
  8. dexverse_cli-0.1.4/src/dexverse_cli/common/__init__.py +24 -0
  9. dexverse_cli-0.1.4/src/dexverse_cli/common/hashes.py +87 -0
  10. {dexverse_cli-0.1.3.post1/src/dexverse_cli → dexverse_cli-0.1.4/src/dexverse_cli/common}/state.py +141 -141
  11. dexverse_cli-0.1.4/src/dexverse_cli/download/__init__.py +16 -0
  12. dexverse_cli-0.1.4/src/dexverse_cli/download/batch.py +246 -0
  13. dexverse_cli-0.1.4/src/dexverse_cli/download/filesystem.py +190 -0
  14. {dexverse_cli-0.1.3.post1/src/dexverse_cli → dexverse_cli-0.1.4/src/dexverse_cli/download}/http.py +467 -416
  15. {dexverse_cli-0.1.3.post1/src/dexverse_cli → dexverse_cli-0.1.4/src/dexverse_cli/download}/manifest.py +356 -271
  16. dexverse_cli-0.1.4/src/dexverse_cli/download/runner.py +110 -0
  17. dexverse_cli-0.1.4/src/dexverse_cli/download/transfer.py +274 -0
  18. dexverse_cli-0.1.4/src/dexverse_cli/progress/__init__.py +25 -0
  19. dexverse_cli-0.1.4/src/dexverse_cli/progress/bar.py +144 -0
  20. dexverse_cli-0.1.4/src/dexverse_cli/progress/formatting.py +58 -0
  21. dexverse_cli-0.1.4/src/dexverse_cli/progress/reporter.py +434 -0
  22. dexverse_cli-0.1.4/src/dexverse_cli/progress/view.py +113 -0
  23. dexverse_cli-0.1.4/src/dexverse_cli/restore/__init__.py +15 -0
  24. {dexverse_cli-0.1.3.post1/src/dexverse_cli → dexverse_cli-0.1.4/src/dexverse_cli/restore}/alignment.py +45 -45
  25. dexverse_cli-0.1.4/src/dexverse_cli/restore/batch.py +116 -0
  26. dexverse_cli-0.1.4/src/dexverse_cli/restore/instruction.py +43 -0
  27. dexverse_cli-0.1.3.post1/src/dexverse_cli/raw_schema.py → dexverse_cli-0.1.4/src/dexverse_cli/restore/layout.py +418 -418
  28. dexverse_cli-0.1.3.post1/src/dexverse_cli/unversioned_hdf5.py → dexverse_cli-0.1.4/src/dexverse_cli/restore/legacy.py +301 -321
  29. dexverse_cli-0.1.4/src/dexverse_cli/restore/media.py +54 -0
  30. dexverse_cli-0.1.3.post1/src/dexverse_cli/transaction.py → dexverse_cli-0.1.4/src/dexverse_cli/restore/publish.py +142 -126
  31. dexverse_cli-0.1.4/src/dexverse_cli/restore/runner.py +70 -0
  32. dexverse_cli-0.1.3.post1/src/dexverse_cli/hdf5_schema.py → dexverse_cli-0.1.4/src/dexverse_cli/restore/schema.py +514 -519
  33. dexverse_cli-0.1.4/src/dexverse_cli/restore/session.py +43 -0
  34. dexverse_cli-0.1.4/src/dexverse_cli/restore/versioned.py +79 -0
  35. dexverse_cli-0.1.4/src/dexverse_cli/update/__init__.py +1 -0
  36. dexverse_cli-0.1.4/src/dexverse_cli/update/check.py +355 -0
  37. dexverse_cli-0.1.4/src/dexverse_cli/update/gate.py +96 -0
  38. dexverse_cli-0.1.4/src/dexverse_cli/update/observation.py +244 -0
  39. dexverse_cli-0.1.4/src/dexverse_cli/update/upgrade.py +203 -0
  40. {dexverse_cli-0.1.3.post1 → dexverse_cli-0.1.4}/uv.lock +3 -24
  41. dexverse_cli-0.1.3.post1/PKG-INFO +0 -123
  42. dexverse_cli-0.1.3.post1/src/dexverse_cli/README.md +0 -107
  43. dexverse_cli-0.1.3.post1/src/dexverse_cli/__init__.py +0 -1
  44. dexverse_cli-0.1.3.post1/src/dexverse_cli/cli.py +0 -194
  45. dexverse_cli-0.1.3.post1/src/dexverse_cli/download.py +0 -476
  46. dexverse_cli-0.1.3.post1/src/dexverse_cli/jetson_encoder.py +0 -168
  47. dexverse_cli-0.1.3.post1/src/dexverse_cli/pack.py +0 -167
  48. dexverse_cli-0.1.3.post1/src/dexverse_cli/unpack.py +0 -185
@@ -1,12 +1,12 @@
1
- .venv/
2
- .venv-smoke/
3
- .venv-smoke-*/
4
- .smoke-work/
5
- .artifact-inspect/
6
- .manifest/
7
- .pytest_cache/
8
- .mypy_cache/
9
- .ruff_cache/
10
- __pycache__/
11
- *.py[cod]
12
- dist/
1
+ .venv/
2
+ .venv-smoke/
3
+ .venv-smoke-*/
4
+ .smoke-work/
5
+ .artifact-inspect/
6
+ .manifest/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ __pycache__/
11
+ *.py[cod]
12
+ dist/
@@ -1 +1 @@
1
- 3.10
1
+ 3.10
@@ -0,0 +1,301 @@
1
+ Metadata-Version: 2.5
2
+ Name: dexverse-cli
3
+ Version: 0.1.4
4
+ Summary: Download DexVerse dataset manifests and unpack HDF5 sessions
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: av<17,>=14
7
+ Requires-Dist: h5py<4,>=3.11
8
+ Requires-Dist: httpx<1,>=0.28
9
+ Requires-Dist: numpy<3,>=1.26
10
+ Requires-Dist: packaging<27,>=24
11
+ Requires-Dist: pillow<13,>=10
12
+ Requires-Dist: rich<15,>=13.9
13
+ Requires-Dist: typer<1,>=0.16
14
+ Description-Content-Type: text/markdown
15
+
16
+ # dexverse-cli
17
+
18
+ dexverse-cli downloads DexVerse dataset manifests over HTTP(S) and unpacks
19
+ HDF5 sessions back to the restored dataset layout. It requires
20
+ Python 3.10 or newer.
21
+
22
+
23
+ ## Install
24
+
25
+ ```console
26
+ pip install dexverse-cli
27
+ ```
28
+
29
+ ## Automatic updates
30
+
31
+ Every business command (`download`, `unpack`, `convert`) first asks whether a
32
+ newer `dexverse-cli` is published. Public PyPI is asked first, because that is
33
+ where a new release appears immediately; when PyPI cannot be reached the check
34
+ falls back to the source this machine's pip is configured with (`pip.conf`,
35
+ `PIP_INDEX_URL`, mirrors, proxies). The check is bounded to about two seconds.
36
+
37
+ - Nothing new, or nothing to ask: the command runs as usual.
38
+ - A newer release exists: the CLI upgrades itself in place with
39
+ `python -m pip install --upgrade dexverse-cli` from the source that answered,
40
+ and then exits with status **20**. Run the same command again; the rerun uses
41
+ the new version.
42
+ - The check times out, pip is missing, no source answers, or the upgrade fails:
43
+ one line is printed and the requested command still runs.
44
+
45
+ The switch is on by default. Turn the check off for one command, or permanently:
46
+
47
+ ```console
48
+ dexverse-dl download --manifest manifest.json --out ./dataset --no-update-check
49
+ set DEXVERSE_CLI_UPDATE=off :: Windows (export ... on Linux and macOS)
50
+ ```
51
+
52
+ To pin one command — or a whole machine — to a specific source instead:
53
+
54
+ ```console
55
+ dexverse-dl download --manifest manifest.json --out ./dataset --update-index-url https://mirror.internal/simple
56
+ set DEXVERSE_CLI_UPDATE_INDEX_URL=https://mirror.internal/simple
57
+ set DEXVERSE_CLI_UPDATE_EXTRA_INDEX_URL=https://mirror.internal/simple
58
+ set DEXVERSE_CLI_UPDATE_TRUSTED_HOST=mirror.internal
59
+ ```
60
+
61
+ The flag is shorthand for `DEXVERSE_CLI_UPDATE_INDEX_URL`. Naming any of these
62
+ replaces both defaults: pip is asked only there, which is also how to skip the
63
+ PyPI attempt on a network that cannot reach it.
64
+
65
+ `dexverse-dl --update-status` prints the recorded observation as JSON without any
66
+ network access or change: whether the switch is on, which source a check would
67
+ use (`update_source`, either `pip` or the index you named), how this interpreter
68
+ was installed (`install_mode`), and the last upgrade this environment performed,
69
+ if any. A command that changes nothing records nothing, so it leaves no directory
70
+ behind. `dexverse-dl --version` never checks. Pre-releases are ignored unless you
71
+ set `DEXVERSE_CLI_UPDATE_PRERELEASE=1`, and a working copy (`pip install -e .`,
72
+ `install_mode: source`) is never checked or upgraded automatically.
73
+
74
+ ## Download
75
+
76
+ Read a manifest from a file:
77
+
78
+ ```console
79
+ dexverse-dl download --manifest manifest.json --out ./dataset
80
+ ```
81
+
82
+ Or pipe a short-lived manifest directly to stdin:
83
+
84
+ ```console
85
+ type manifest.json | dexverse-dl download --manifest - --out ./dataset
86
+ ```
87
+
88
+ A manifest is either a JSON file list or an object containing `files`. Each
89
+ file requires `download_url` and `file_name`. Optional fields are
90
+ `storage_path`, `file_size`, `sha256`, `hash`, `hash_type`, `meta_data`, and
91
+ `data_protocol`. Unknown top-level keys next to `files` (for example the
92
+ `query_conditions` / `export_info` blocks written by the web batch export) are
93
+ ignored. `data_protocol` is retained in the downloaded metadata sidecar.
94
+ Downloaded metadata is stored in a versioned sidecar and becomes
95
+ `task_instruction.json` when the corresponding HDF5 file is unpacked; only
96
+ top-level `data_protocol` is removed from that algorithm-facing projection.
97
+
98
+ ### Digests
99
+
100
+ A manifest may declare one digest per file. `hash` and `hash_type` are a pair
101
+ and must appear together, so the tool never guesses an algorithm; the legacy
102
+ `sha256` field is the one exception, since its name already carries the type.
103
+ Three types are accepted:
104
+
105
+ | `hash_type` | Meaning | Verification cost |
106
+ | --- | --- | --- |
107
+ | `sha256` | Whole-file SHA-256 | Reads the whole file after download |
108
+ | `md5` | Whole-file MD5, for older records | Reads the whole file after download |
109
+ | `sample_sha256_v1` | Bounded sample over the head, middle, and tail 1 MiB blocks, framed with the file size | Reads at most 3 MiB per file |
110
+
111
+ `sample_sha256_v1` matches the digest the uploader and the web console already
112
+ produce, so their recorded value can be reused directly. It is a deduplication
113
+ fingerprint, not a full checksum: a change outside the sampled windows cannot be
114
+ detected. It does catch truncation and any change inside a sampled window.
115
+ `md5` is fine for detecting transfer corruption but is weak against a deliberate
116
+ collision, so it is not a security boundary.
117
+
118
+ Digests are **not** checked unless you ask for it, because hashing large files
119
+ costs real time:
120
+
121
+ ```console
122
+ dexverse-dl download --manifest manifest.json --out ./dataset --verify
123
+ ```
124
+
125
+ Without `--verify`, only `file_size` is enforced (when the manifest declares
126
+ it), and the download state records no digest — so a later `--verify` run still
127
+ rechecks the file instead of trusting an unchecked claim. With `--verify`, a
128
+ mismatch removes the partial and fails that file.
129
+
130
+ The download command renders one progress display per batch: a batch line
131
+ (percent, file counter, transferred bytes, rate, remaining time, elapsed time)
132
+ and a line naming the current file. A retry prints a single persistent warning
133
+ above the display and updates the same progress line, so no second progress bar
134
+ is ever added:
135
+
136
+ ```console
137
+ Download ━━━━━━━━━━━━━━─────────────────────── 38% 13/33 88.0 GB/228.3 GB 19.4 MB/s 1:15:47 < 2:00:49
138
+ downloading: piper_v02_0920_raw/episode_000007.hdf5
139
+ warning: retrying piper_v02_0920_raw/episode_000012.hdf5 (attempt 2/5) in 2.0s: transient HTTP status 503
140
+ Download ━━━━━━━━━━━━━━──────────────────────── 38% 13/33 1 failed 88.0 GB/228.3 GB 19.4 MB/s 1:15:47 < 2:00:49
141
+ retrying: piper_v02_0920_raw/episode_000012.hdf5 (retry 2/5) - transient HTTP status 503; next attempt in 2.0s
142
+ ```
143
+
144
+ The bar is a filled rail drawn with rich's own glyphs: `━` for a filled cell and
145
+ `─` for the rest. There is no frame around it, and no mark drawn inside the empty
146
+ part either: unlike a space-filled bar the rail delimits itself, which is why
147
+ `pip`, `rich`, and `tqdm` leave the brackets out. rich builds its rail from one
148
+ glyph and shows the split with colour, which is why rich can afford half cells;
149
+ here the two weights carry the split, and that rules half cells out. A half cell of
150
+ fill is heavy on one side of its cell and blank on the other, while a light rail
151
+ can only start at the next boundary, so the junction gains and loses half a cell
152
+ as the fill crosses each one — a notch that blinks. The fill therefore ends on
153
+ cell boundaries only and meets the rail directly, so every frame has one junction
154
+ shape and only its position moves; the percentage and byte counters carry the finer
155
+ resolution. Holding a permanent half-cell gap open at the junction was tried as
156
+ well, and reads worse than meeting flush, so there is none. The fill is drawn in
157
+ the line's own colour and only the rest of the rail steps back to a dim shade, so
158
+ the bar never competes with the numbers. Because the empty part differs by glyph
159
+ and not by colour alone, progress stays readable in a piped log and under
160
+ `NO_COLOR`. Counters and byte totals carry no words — `13/33` and
161
+ `88.0 GB/228.3 GB` are self-describing, as they are in every common progress
162
+ line — and the two clocks are paired the way `tqdm` pairs them, elapsed on the
163
+ left of `<` and remaining on the right, so neither needs a label and neither can
164
+ be mistaken for the other. The right side stays `--:--` while the total is
165
+ incomplete, since only a complete total yields an estimate.
166
+
167
+ The rail keeps one length for the whole batch: the first frame fixes it, so a
168
+ field that appears or grows mid-run (a failure count, a byte total crossing into
169
+ the next unit, the elapsed clock) cannot take cells from it. A shorter bar would
170
+ re-render a shorter fill and read as progress going backwards. That first frame
171
+ also holds room for the failure count before anything has failed, so the first
172
+ failure cannot push the remaining time off the end of the line. Only a terminal
173
+ resize measures the rail again, and on a narrow terminal the tail is clipped
174
+ before the rail is. The rail is up to 40 cells — long enough that the first one
175
+ fills at about 2.5%, since a bar that only starts moving at 4% is doing less to
176
+ calm the wait than it could — and it takes whatever fewer columns a narrow
177
+ terminal leaves over.
178
+
179
+ The live region is rebuilt on rich's refresh tick as well as whenever new state
180
+ arrives, so the elapsed clock keeps counting while one file is being worked on.
181
+ Without that tick the clock would freeze between callbacks and then jump by the
182
+ length of the file, which reads as a stall.
183
+
184
+ Percent, byte counters, and the bar follow transferred bytes; the file counter
185
+ advances when a file is published or skipped. When the manifest omits
186
+ `file_size` for any entry, the total is shown as a lower bound (`~6.3 GB`) that
187
+ grows as each transfer reports its real size, and the bar stays an empty rail
188
+ rather than filling against a bound that is still growing; percent and remaining
189
+ time are withheld until the total is complete instead of being guessed from a
190
+ partial sum. Pass `--probe-sizes` to read those sizes from the server first (one
191
+ small ranged request per entry that lacks one), so the total, percentage, and
192
+ remaining time are correct before the first byte moves:
193
+
194
+ ```console
195
+ dexverse-dl download --manifest manifest.json --out ./dataset --probe-sizes
196
+ ```
197
+
198
+ The probe is off by default because a complete manifest needs no extra requests.
199
+ It costs one `GET` with `Range: bytes=0-0` per unknown entry and tolerates a
200
+ server that ignores the range. Piped or logged output (no terminal) prints the
201
+ same line periodically instead of repainting in place, so redirecting to a file
202
+ stays readable.
203
+
204
+ Pass `--no-progress` to suppress the live display, the per-file line, and retry
205
+ warnings while keeping the final summary:
206
+
207
+ ```console
208
+ dexverse-dl download --manifest manifest.json --out ./dataset --no-progress
209
+ ```
210
+
211
+
212
+ Downloads use bounded parallelism, retries, and compatible partial-file resume.
213
+ Within a manifest, the first occurrence of a target path keeps its name; later
214
+ occurrences receive `-1`, `-2`, etc. before the final extension (for example,
215
+ `episode1.hdf5`, `episode1-1.hdf5`, `episode1-2.hdf5`). Numbering skips names and
216
+ paths already claimed by other entries, including partial files and metadata.
217
+ Different relative directories remain independent. Names are assigned in manifest
218
+ order, with Windows case-insensitive matching, before any downloads start.
219
+ Rerunning the same manifest preserves the assigned names for skipping and resume;
220
+ existing files on disk do not trigger new suffixes. This is not cross-batch name
221
+ allocation: changing the manifest entries or their order can change the mapping.
222
+ When a digest is declared it is checked only under `--verify`; without it a file
223
+ is accepted on its declared `file_size` alone. TLS certificate verification is
224
+ disabled by default for internal endpoints; pass `--secure` to enable
225
+ certificate verification.
226
+
227
+ ## Unpack
228
+
229
+ Unpack one HDF5 file to the restored raw dataset layout:
230
+
231
+ ```console
232
+ dexverse-dl unpack --input ./packed/session.hdf5 --output ./restored
233
+ ```
234
+
235
+ Or unpack every `*.hdf5` under a directory, preserving relative paths:
236
+
237
+ ```console
238
+ dexverse-dl unpack --input-dir ./packed --output ./restored
239
+ ```
240
+
241
+ Unpacking validates the complete versioned schema before transactionally
242
+ publishing a restored session, and retains the supported unversioned HDF5 read
243
+ path. It accepts `--progress/--no-progress`; the batch line reports the same
244
+ filled rail as the download command, driven by restored sessions, plus the
245
+ session count, the restored frame count, and the elapsed time:
246
+
247
+ ```console
248
+ Unpacking ━━━━━━━━━━━━━─────────────────────────── 33% 3/9 1200 frames restored 00:42
249
+ packed/episode_000003.hdf5 -> restored/episode_000003
250
+ ```
251
+
252
+ The rail is empty until the first callback reports how many sessions the input
253
+ holds, and shows `?%` until then. That batch has no rate, so it has no estimate
254
+ and only one clock, printed bare because there is nothing to tell it apart from.
255
+ The frame count is the running total over the whole batch, not the count for the
256
+ session named on the line below it, which is why it is spelled out as `frames
257
+ restored`: it only ever counts sessions that finished, so a failed session adds
258
+ nothing, and it reaches its final value in the summary line. It is always printed,
259
+ even at zero, so the line keeps one shape from the first frame to the last.
260
+ Batches continue after an item failure but exit with status 1 if any item fails.
261
+
262
+ ## Convert
263
+
264
+ `convert` is kept as a compatibility alias for `unpack`, because pipelines that
265
+ were written against the earlier releases still call it. It takes the same
266
+ options and performs the same restore:
267
+
268
+ ```console
269
+ dexverse-dl convert --input ./packed/session.hdf5 --output ./restored
270
+ dexverse-dl convert --input-dir ./packed --output ./restored
271
+ ```
272
+
273
+ Its only differences are the labels on the progress line and in the summary:
274
+ `Converting` and `converted=` instead of `Unpacking` and `unpacked=`. New
275
+ scripts should prefer `unpack`.
276
+
277
+ ## Code layout
278
+
279
+ `cli.py` only wires typer to the chains; everything else lives in the package for
280
+ the chain it belongs to, and the two chains never import each other.
281
+
282
+ ```
283
+ dexverse_cli/
284
+ cli.py download / unpack / convert, and nothing else
285
+ common/ what both chains share: the .dexverse state directory, digests
286
+ progress/ what a running command shows: views, the rail, the reporters
287
+ view.py the frozen snapshots a batch hands to its display
288
+ bar.py the rail's glyphs, geometry, and styling
289
+ formatting.py byte counts and clocks as the lines show them
290
+ reporter.py the live two-line region and the reports that drive it
291
+ download/ manifest -> plan -> one file -> batch -> runner
292
+ restore/ session format -> raw layout -> publish -> batch -> runner
293
+ ```
294
+
295
+ Each chain follows the same shape, so a reader who knows one knows the other: an
296
+ input contract (`download/manifest.py`, `restore/schema.py`), a plan
297
+ (`download/filesystem.py`, `restore/versioned.py`), the work for one item
298
+ (`download/transfer.py` `FileTransfer`, `restore/session.py` `SessionRestore`), the
299
+ one owner of the batch's state and counters (`*/batch.py`), and the orchestration
300
+ that decides the order (`*/runner.py`).
301
+
@@ -1,68 +1,69 @@
1
- [build-system]
2
- requires = ["hatchling>=1.27"]
3
- build-backend = "hatchling.build"
4
-
5
- [project]
6
- name = "dexverse-cli"
7
- version = "0.1.3.post1"
8
- description = "Download, pack, and unpack DexVerse datasets"
9
- readme = "src/dexverse_cli/README.md"
10
- requires-python = ">=3.10"
11
- dependencies = [
12
- "av>=14,<17",
13
- "h5py>=3.11,<4",
14
- "httpx>=0.28,<1",
15
- "numpy>=1.26,<3",
16
- "pillow>=10,<13",
17
- "rich>=13.9,<15",
18
- "typer>=0.16,<1",
19
- ]
20
-
21
- [project.optional-dependencies]
22
- jetson = [
23
- "PyGObject>=3.42,<4; platform_system == 'Linux'",
24
- ]
25
-
26
- [project.scripts]
27
- dexverse-dl = "dexverse_cli.cli:app"
28
-
29
- [dependency-groups]
30
- dev = [
31
- "mypy>=1.15,<2",
32
- "pytest>=8.3,<10",
33
- "ruff>=0.11,<1",
34
- ]
35
-
36
- [tool.hatch.build.targets.wheel]
37
- packages = ["src/dexverse_cli"]
38
-
39
- [tool.hatch.build.targets.sdist]
40
- include = [
41
- "/pyproject.toml",
42
- "/uv.lock",
43
- "/.python-version",
44
- "/src/dexverse_cli",
45
- ]
46
-
47
- [tool.pytest.ini_options]
48
- addopts = "-ra"
49
- testpaths = ["tests"]
50
-
51
- [tool.ruff]
52
- target-version = "py310"
53
- line-length = 120
54
-
55
- [tool.ruff.lint]
56
- select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "TID252"]
57
-
58
- [tool.ruff.lint.flake8-tidy-imports]
59
- ban-relative-imports = "all"
60
-
61
- [tool.ruff.lint.per-file-ignores]
62
- "tests/**/*.py" = ["TID252"]
63
-
64
- [tool.mypy]
65
- python_version = "3.10"
66
- strict = true
67
- warn_unreachable = true
68
- ignore_missing_imports = true
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "dexverse-cli"
7
+ dynamic = ["version"]
8
+ description = "Download DexVerse dataset manifests and unpack HDF5 sessions"
9
+ readme = "src/dexverse_cli/README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "av>=14,<17",
13
+ "h5py>=3.11,<4",
14
+ "httpx>=0.28,<1",
15
+ "numpy>=1.26,<3",
16
+ "packaging>=24,<27",
17
+ "pillow>=10,<13",
18
+ "rich>=13.9,<15",
19
+ "typer>=0.16,<1",
20
+ ]
21
+
22
+ [project.scripts]
23
+ dexverse-dl = "dexverse_cli.cli:app"
24
+
25
+ [dependency-groups]
26
+ dev = [
27
+ "mypy>=1.15,<2",
28
+ "pytest>=8.3,<10",
29
+ "ruff>=0.11,<1",
30
+ ]
31
+
32
+ [tool.hatch.version]
33
+ # One version, declared once: the build backend reads the package marker, so a
34
+ # release bump cannot leave the wheel metadata disagreeing with the code.
35
+ path = "src/dexverse_cli/__init__.py"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/dexverse_cli"]
39
+
40
+ [tool.hatch.build.targets.sdist]
41
+ include = [
42
+ "/pyproject.toml",
43
+ "/uv.lock",
44
+ "/.python-version",
45
+ "/src/dexverse_cli",
46
+ ]
47
+
48
+ [tool.pytest.ini_options]
49
+ addopts = "-ra"
50
+ testpaths = ["tests"]
51
+
52
+ [tool.ruff]
53
+ target-version = "py310"
54
+ line-length = 120
55
+
56
+ [tool.ruff.lint]
57
+ select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "TID252"]
58
+
59
+ [tool.ruff.lint.flake8-tidy-imports]
60
+ ban-relative-imports = "all"
61
+
62
+ [tool.ruff.lint.per-file-ignores]
63
+ "tests/**/*.py" = ["TID252"]
64
+
65
+ [tool.mypy]
66
+ python_version = "3.10"
67
+ strict = true
68
+ warn_unreachable = true
69
+ ignore_missing_imports = true