ds-crawler 2.13.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 (58) hide show
  1. ds_crawler-2.13.0/.gitignore +23 -0
  2. ds_crawler-2.13.0/CHANGELOG.md +5 -0
  3. ds_crawler-2.13.0/CONTRIBUTING.md +46 -0
  4. ds_crawler-2.13.0/LICENSE +21 -0
  5. ds_crawler-2.13.0/PKG-INFO +342 -0
  6. ds_crawler-2.13.0/README.md +309 -0
  7. ds_crawler-2.13.0/SECURITY.md +15 -0
  8. ds_crawler-2.13.0/docs/configuration.md +222 -0
  9. ds_crawler-2.13.0/docs/validated-publication.md +65 -0
  10. ds_crawler-2.13.0/ds_crawler/__init__.py +119 -0
  11. ds_crawler-2.13.0/ds_crawler/_dataset_contract.py +45 -0
  12. ds_crawler-2.13.0/ds_crawler/_version.py +13 -0
  13. ds_crawler-2.13.0/ds_crawler/artifact_builder.py +255 -0
  14. ds_crawler-2.13.0/ds_crawler/artifacts.py +247 -0
  15. ds_crawler-2.13.0/ds_crawler/cli.py +375 -0
  16. ds_crawler-2.13.0/ds_crawler/config.py +509 -0
  17. ds_crawler-2.13.0/ds_crawler/handlers/__init__.py +36 -0
  18. ds_crawler-2.13.0/ds_crawler/handlers/base.py +30 -0
  19. ds_crawler-2.13.0/ds_crawler/handlers/generic.py +28 -0
  20. ds_crawler-2.13.0/ds_crawler/handlers/zip_handler.py +53 -0
  21. ds_crawler-2.13.0/ds_crawler/layout.py +165 -0
  22. ds_crawler-2.13.0/ds_crawler/migration.py +775 -0
  23. ds_crawler-2.13.0/ds_crawler/operations.py +1994 -0
  24. ds_crawler-2.13.0/ds_crawler/parser.py +831 -0
  25. ds_crawler-2.13.0/ds_crawler/path_filters.py +229 -0
  26. ds_crawler-2.13.0/ds_crawler/records.py +462 -0
  27. ds_crawler-2.13.0/ds_crawler/schema.py +159 -0
  28. ds_crawler-2.13.0/ds_crawler/traversal.py +391 -0
  29. ds_crawler-2.13.0/ds_crawler/validation.py +444 -0
  30. ds_crawler-2.13.0/ds_crawler/writer.py +758 -0
  31. ds_crawler-2.13.0/ds_crawler/zip_utils.py +722 -0
  32. ds_crawler-2.13.0/examples/augmented_rgb_example.py +175 -0
  33. ds_crawler-2.13.0/examples/basic_usage.py +48 -0
  34. ds_crawler-2.13.0/meta/__init__.py +0 -0
  35. ds_crawler-2.13.0/meta/build_meta_schema.py +26 -0
  36. ds_crawler-2.13.0/meta/schema.json +374 -0
  37. ds_crawler-2.13.0/pyproject.toml +78 -0
  38. ds_crawler-2.13.0/tests/__init__.py +0 -0
  39. ds_crawler-2.13.0/tests/current_helpers.py +133 -0
  40. ds_crawler-2.13.0/tests/test_align_datasets.py +65 -0
  41. ds_crawler-2.13.0/tests/test_artifact_builder.py +172 -0
  42. ds_crawler-2.13.0/tests/test_attributes_passthrough.py +111 -0
  43. ds_crawler-2.13.0/tests/test_config.py +137 -0
  44. ds_crawler-2.13.0/tests/test_copy_dataset.py +62 -0
  45. ds_crawler-2.13.0/tests/test_extract_datasets.py +87 -0
  46. ds_crawler-2.13.0/tests/test_handlers.py +44 -0
  47. ds_crawler-2.13.0/tests/test_inline_splits.py +365 -0
  48. ds_crawler-2.13.0/tests/test_integration.py +200 -0
  49. ds_crawler-2.13.0/tests/test_layout.py +59 -0
  50. ds_crawler-2.13.0/tests/test_meta_schema.py +51 -0
  51. ds_crawler-2.13.0/tests/test_metadata_scopes.py +312 -0
  52. ds_crawler-2.13.0/tests/test_new_metadata.py +474 -0
  53. ds_crawler-2.13.0/tests/test_parser.py +189 -0
  54. ds_crawler-2.13.0/tests/test_records.py +236 -0
  55. ds_crawler-2.13.0/tests/test_split_dataset.py +72 -0
  56. ds_crawler-2.13.0/tests/test_validation.py +125 -0
  57. ds_crawler-2.13.0/tests/test_writer.py +169 -0
  58. ds_crawler-2.13.0/tests/test_zip.py +63 -0
@@ -0,0 +1,23 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .coverage
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .venv/
9
+ build/
10
+ dist/
11
+
12
+ # Editors and operating systems
13
+ .DS_Store
14
+ .vscode/
15
+
16
+ # Local tooling and agent state
17
+ .ao-mcp/
18
+ .ao-provider-auth/
19
+ .claude/
20
+
21
+ # Local datasets and scratch output
22
+ /cluster/
23
+ /SHOULD_MATCH
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 2.11.0 (unreleased)
4
+
5
+ Add public detached-head APIs, validated byte commits, atomic metadata generations/ZIP publication, resume, and receipt-preserving copy/split/scopes. No tensor execution semantics.
@@ -0,0 +1,46 @@
1
+ # Contributing
2
+
3
+ Thanks for improving `ds-crawler`. Small, focused changes with tests are the
4
+ easiest to review.
5
+
6
+ ## Set up a development environment
7
+
8
+ The project uses Python 3.9 or newer and [uv](https://docs.astral.sh/uv/):
9
+
10
+ ```bash
11
+ git clone https://github.com/d-rothen/ds-crawler.git
12
+ cd ds-crawler
13
+ uv sync --extra dev
14
+ ```
15
+
16
+ ## Before opening a pull request
17
+
18
+ Run the same core checks as CI:
19
+
20
+ ```bash
21
+ uv run ruff check .
22
+ uv run pytest
23
+ uv build
24
+ uv run python scripts/verify_distribution.py dist/*
25
+ ```
26
+
27
+ When changing modality metadata, regenerate the checked-in schema and run its
28
+ test:
29
+
30
+ ```bash
31
+ uv run build-meta-schema
32
+ uv run pytest tests/test_meta_schema.py
33
+ ```
34
+
35
+ Please include a regression test for behavior changes and update the README or
36
+ configuration reference when public behavior changes. Avoid committing dataset
37
+ archives, generated build output, local paths, credentials, or editor state.
38
+
39
+ ## Reporting bugs
40
+
41
+ Open a GitHub issue with a minimal configuration, representative relative file
42
+ paths, the expected result, and the full error. Remove private dataset paths or
43
+ metadata before posting.
44
+
45
+ Security issues follow a separate private process described in
46
+ [SECURITY.md](SECURITY.md).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Rothenpieler <rothenpielerdaniel@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,342 @@
1
+ Metadata-Version: 2.5
2
+ Name: ds-crawler
3
+ Version: 2.13.0
4
+ Summary: Regex-based dataset crawler, indexer, and artifact toolkit
5
+ Project-URL: Homepage, https://github.com/d-rothen/ds-crawler
6
+ Project-URL: Issues, https://github.com/d-rothen/ds-crawler/issues
7
+ Project-URL: Repository, https://github.com/d-rothen/ds-crawler
8
+ Author-email: Daniel Rothenpieler <rothenpielerdaniel@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: dataset,indexing,machine-learning,metadata,zip
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: euler-dataset-contract>=0.8.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest; extra == 'dev'
28
+ Requires-Dist: ruff; extra == 'dev'
29
+ Requires-Dist: tqdm; extra == 'dev'
30
+ Provides-Extra: progress
31
+ Requires-Dist: tqdm; extra == 'progress'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # ds-crawler
35
+
36
+ [![CI](https://github.com/d-rothen/ds-crawler/actions/workflows/ci.yml/badge.svg)](https://github.com/d-rothen/ds-crawler/actions/workflows/ci.yml)
37
+ [![PyPI](https://img.shields.io/pypi/v/ds-crawler.svg)](https://pypi.org/project/ds-crawler/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/ds-crawler.svg)](https://pypi.org/project/ds-crawler/)
39
+
40
+ Build stable, queryable metadata for datasets whose structure is encoded in
41
+ file paths.
42
+
43
+ `ds-crawler` scans directories or ZIP archives, extracts IDs, hierarchy, and
44
+ properties with regular expressions, and writes a small set of versioned JSON
45
+ artifacts. It also aligns modalities, creates reproducible splits, copies or
46
+ extracts subsets, and indexes generated outputs.
47
+
48
+ - Directory and `.zip` inputs use the same API.
49
+ - Regex capture groups define IDs, hierarchy, and file properties.
50
+ - Include/exclude filters keep irrelevant files out of the index.
51
+ - Named splits and metadata scopes are first-class artifacts.
52
+ - Dataset semantics live in the shared `euler-dataset-contract` format.
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install ds-crawler
58
+ ```
59
+
60
+ Progress bars are optional:
61
+
62
+ ```bash
63
+ pip install "ds-crawler[progress]"
64
+ ```
65
+
66
+ Python 3.9 or newer is required.
67
+
68
+ ## Quick start
69
+
70
+ Given this dataset:
71
+
72
+ ```text
73
+ foggy_rgb/
74
+ ├── .ds_crawler/
75
+ │ ├── dataset-head.json
76
+ │ └── ds-crawler.json
77
+ ├── scene_01/
78
+ │ ├── 0001.png
79
+ │ └── 0002.png
80
+ └── scene_02/
81
+ └── 0001.png
82
+ ```
83
+
84
+ describe its semantic identity in `.ds_crawler/dataset-head.json`:
85
+
86
+ ```json
87
+ {
88
+ "contract": {"kind": "dataset_head", "version": "1.0"},
89
+ "dataset": {"id": "foggy_rgb", "name": "Foggy RGB"},
90
+ "modality": {
91
+ "key": "rgb",
92
+ "meta": {"range": [0, 255]}
93
+ },
94
+ "addons": {
95
+ "euler_train": {
96
+ "version": "1.0",
97
+ "used_as": "input",
98
+ "slot": "dehaze.input.rgb"
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ Then describe how paths become index entries in
105
+ `.ds_crawler/ds-crawler.json`:
106
+
107
+ ```json
108
+ {
109
+ "contract": {"kind": "ds_crawler_config", "version": "2.0"},
110
+ "head_file": "dataset-head.json",
111
+ "source": {"path": "."},
112
+ "indexing": {
113
+ "id": {
114
+ "regex": "^(?P<scene>[^/]+)/(?P<frame>\\d+)\\.png$",
115
+ "join_char": "+"
116
+ },
117
+ "hierarchy": {
118
+ "regex": "^(?P<scene>[^/]+)/",
119
+ "separator": ":"
120
+ },
121
+ "properties": {
122
+ "basename": {
123
+ "regex": "^(?P<frame>\\d+)\\.(?P<ext>png)$"
124
+ }
125
+ },
126
+ "files": {"extensions": [".png"]},
127
+ "constraints": {"flat_ids_unique": true}
128
+ }
129
+ }
130
+ ```
131
+
132
+ Index and persist the dataset:
133
+
134
+ ```python
135
+ from ds_crawler import get_files, index_dataset_from_path
136
+
137
+ output = index_dataset_from_path("/data/foggy_rgb", save_index=True)
138
+
139
+ print(output["head"]["dataset"]["name"])
140
+ print(get_files(output))
141
+ ```
142
+
143
+ The returned object is hydrated with the dataset head and indexing recipe for
144
+ convenience. The saved `index.json` stays minimal.
145
+
146
+ For a self-contained example that creates a temporary dataset, see
147
+ [`examples/basic_usage.py`](examples/basic_usage.py).
148
+
149
+ ## Artifact layout
150
+
151
+ For a physical root containing several logical modalities, prefer the scoped
152
+ layout. Each modality gets a complete, isolated artifact set and `scopes.json`
153
+ provides the discovery manifest:
154
+
155
+ ```text
156
+ foggy_rgb/
157
+ ├── .ds_crawler/
158
+ │ ├── scopes.json
159
+ │ ├── rgb/
160
+ │ │ ├── dataset-head.json
161
+ │ │ ├── ds-crawler.json
162
+ │ │ ├── index.json
163
+ │ │ └── split_train.json
164
+ │ └── camera_extrinsics/
165
+ │ ├── dataset-head.json
166
+ │ ├── ds-crawler.json
167
+ │ └── index.json
168
+ └── ...
169
+ ```
170
+
171
+ Pass `metadata_scope="rgb"` (or another safe modality name) to path-based APIs
172
+ to read and write `.ds_crawler/rgb/`. Scoped writes maintain `scopes.json`
173
+ automatically.
174
+
175
+ For a root with just one logical dataset, the compact unscoped layout remains
176
+ supported:
177
+
178
+ ```text
179
+ foggy_rgb/
180
+ ├── .ds_crawler/
181
+ │ ├── dataset-head.json
182
+ │ ├── ds-crawler.json
183
+ │ ├── index.json
184
+ │ └── split_train.json
185
+ └── ...
186
+ ```
187
+
188
+ The files in either artifact set are:
189
+
190
+ | File | Purpose |
191
+ |---|---|
192
+ | `dataset-head.json` | Dataset identity, modality metadata, and namespaced addons. |
193
+ | `ds-crawler.json` | Source and indexing recipe. |
194
+ | `index.json` | Materialized recursive file index. |
195
+ | `split_<name>.json` | A named, filtered index with provenance. |
196
+
197
+ See [the configuration reference](docs/configuration.md) for every supported
198
+ field, artifact shape, path filter, and hierarchy pattern.
199
+
200
+ ## Common workflows
201
+
202
+ ### Use the CLI
203
+
204
+ A CLI config can reference one or more datasets that already contain crawler
205
+ metadata:
206
+
207
+ ```json
208
+ {
209
+ "datasets": [
210
+ {"path": "/data/foggy_rgb"},
211
+ {"path": "/data/foggy_depth"}
212
+ ]
213
+ }
214
+ ```
215
+
216
+ ```bash
217
+ ds-crawler index datasets.json
218
+ ```
219
+
220
+ `index` is optional for backwards compatibility, so `ds-crawler datasets.json`
221
+ is equivalent. Useful flags include:
222
+
223
+ | Flag | Effect |
224
+ |---|---|
225
+ | `--strict` | Abort on duplicate IDs. |
226
+ | `--sample N` | Keep every Nth matched file deterministically. |
227
+ | `--match-index PATH` | Keep only hierarchy-qualified IDs found in another index. |
228
+ | `--metadata-scope SCOPE` | Read and write a scoped artifact set. |
229
+ | `--workdir PATH` | Resolve relative dataset paths from a specific directory. |
230
+ | `--verbose` | Log each skipped file. |
231
+
232
+ ### Align modalities
233
+
234
+ ```python
235
+ from ds_crawler import align_datasets
236
+
237
+ aligned = align_datasets(
238
+ {"modality": "rgb", "source": "/data/foggy_rgb"},
239
+ {"modality": "depth", "source": "/data/foggy_depth"},
240
+ )
241
+
242
+ rgb_path = aligned["scene-scene_01+frame-0001"]["rgb"]["path"]
243
+ ```
244
+
245
+ Alignment is by leaf ID. For modalities that apply at an ancestor hierarchy
246
+ level, preserve the hierarchy and join in the consumer instead; the runnable
247
+ [`augmented RGB example`](examples/augmented_rgb_example.py) demonstrates that
248
+ layout.
249
+
250
+ ### Create reproducible splits
251
+
252
+ ```python
253
+ from ds_crawler import create_dataset_splits, load_dataset_split
254
+
255
+ create_dataset_splits(
256
+ "/data/foggy_rgb",
257
+ split_names=["train", "validation"],
258
+ ratios=[80, 20],
259
+ seed=42,
260
+ )
261
+
262
+ train = load_dataset_split("/data/foggy_rgb", "train")
263
+ ```
264
+
265
+ Use `create_aligned_dataset_splits` for several modalities,
266
+ `create_hierarchy_dataset_splits` for hierarchy rules, or
267
+ `create_mapped_dataset_splits` for an explicit qualified-ID mapping.
268
+ `copy_dataset_splits` transfers an existing partition to a sibling dataset and
269
+ fails before writing if any target ID is missing.
270
+
271
+ ### Write generated data
272
+
273
+ ```python
274
+ from ds_crawler import DatasetWriter
275
+
276
+ head = {
277
+ "contract": {"kind": "dataset_head", "version": "1.0"},
278
+ "dataset": {"id": "predicted_rgb", "name": "Predicted RGB"},
279
+ "modality": {"key": "rgb", "meta": {"range": [0, 255]}},
280
+ }
281
+
282
+ writer = DatasetWriter("/data/predicted_rgb", head=head)
283
+ path = writer.get_path("/scene:scene_01/frame-0001", "0001.png")
284
+ path.write_bytes(encoded_image)
285
+ writer.save_index()
286
+ ```
287
+
288
+ `ZipDatasetWriter` provides the same index-building behavior for a new ZIP
289
+ archive and accepts bytes or writable file-like objects.
290
+
291
+ ### Migrate legacy metadata
292
+
293
+ Normal loading is intentionally strict about the current schema. Migrate older
294
+ `output.json`-based datasets explicitly:
295
+
296
+ ```bash
297
+ ds-crawler migrate-metadata /data/legacy_dataset
298
+ ds-crawler migrate-metadata /data/archive.zip
299
+ ds-crawler migrate-metadata /data/datasets --scan-zips
300
+ ```
301
+
302
+ Run `ds-crawler migrate-metadata --help` for archive scanning, inline split,
303
+ and metadata-scope options.
304
+
305
+ ## Python API
306
+
307
+ The main entry points are re-exported from `ds_crawler`:
308
+
309
+ | Area | Entry points |
310
+ |---|---|
311
+ | Build | `build_dataset_head`, `build_crawler_config`, `build_dataset_artifacts_from_files` |
312
+ | Index | `index_dataset`, `index_dataset_from_files`, `index_dataset_from_path` |
313
+ | Inspect | `get_files`, `collect_qualified_ids`, `get_dataset_contract`, `get_dataset_properties` |
314
+ | Split | `create_dataset_splits`, `create_aligned_dataset_splits`, `create_hierarchy_dataset_splits`, `create_mapped_dataset_splits`, `load_dataset_split` |
315
+ | Transform | `align_datasets`, `copy_dataset`, `extract_datasets`, `split_dataset`, `split_datasets` |
316
+ | Write | `DatasetWriter`, `ZipDatasetWriter` |
317
+ | Validate | `validate_crawler_config`, `validate_dataset`, `validate_output` |
318
+
319
+ All core path-based workflows support directories and ZIP archives. Scope-aware
320
+ operations accept `metadata_scope`; multi-source operations also expose
321
+ per-source scope arguments where needed.
322
+
323
+ ## Development
324
+
325
+ ```bash
326
+ git clone https://github.com/d-rothen/ds-crawler.git
327
+ cd ds-crawler
328
+ uv sync --extra dev
329
+ uv run pytest
330
+ uv run ruff check .
331
+ uv build
332
+ ```
333
+
334
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting a change. To
335
+ report a vulnerability, follow [SECURITY.md](SECURITY.md).
336
+
337
+ ## License
338
+
339
+ Licensed under the [MIT License](LICENSE). Copyright © 2026 Daniel Rothenpieler
340
+ <rothenpielerdaniel@gmail.com>.
341
+
342
+ See [Phase 2 validated-publication](docs/validated-publication.md) for the opt-in captured spatial workflow (2.11.0, unreleased).