dexverse-cli 0.1.1__tar.gz → 0.1.3__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.
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/PKG-INFO +10 -1
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/pyproject.toml +1 -1
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/README.md +9 -0
- dexverse_cli-0.1.3/src/dexverse_cli/__init__.py +1 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/download.py +8 -14
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/manifest.py +34 -10
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/uv.lock +1 -1
- dexverse_cli-0.1.1/src/dexverse_cli/__init__.py +0 -1
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/.gitignore +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/.python-version +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/alignment.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/cli.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/hdf5_schema.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/http.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/jetson_encoder.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/pack.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/raw_schema.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/state.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/transaction.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/unpack.py +0 -0
- {dexverse_cli-0.1.1 → dexverse_cli-0.1.3}/src/dexverse_cli/unversioned_hdf5.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: dexverse-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Download, pack, and unpack DexVerse datasets
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Requires-Dist: av<17,>=14
|
|
@@ -49,6 +49,15 @@ corresponding HDF5 file is unpacked; only top-level `data_protocol` is removed
|
|
|
49
49
|
from that algorithm-facing projection.
|
|
50
50
|
|
|
51
51
|
Downloads use bounded parallelism, retries, and compatible partial-file resume.
|
|
52
|
+
Within a manifest, the first occurrence of a target path keeps its name; later
|
|
53
|
+
occurrences receive `-1`, `-2`, etc. before the final extension (for example,
|
|
54
|
+
`episode1.hdf5`, `episode1-1.hdf5`, `episode1-2.hdf5`). Numbering skips names and
|
|
55
|
+
paths already claimed by other entries, including partial files and metadata.
|
|
56
|
+
Different relative directories remain independent. Names are assigned in manifest
|
|
57
|
+
order, with Windows case-insensitive matching, before any downloads start.
|
|
58
|
+
Rerunning the same manifest preserves the assigned names for skipping and resume;
|
|
59
|
+
existing files on disk do not trigger new suffixes. This is not cross-batch name
|
|
60
|
+
allocation: changing the manifest entries or their order can change the mapping.
|
|
52
61
|
When `sha256` is present it is always checked before the final file is replaced.
|
|
53
62
|
TLS certificate verification is disabled by default for internal endpoints;
|
|
54
63
|
pass `--secure` to enable certificate verification.
|
|
@@ -33,6 +33,15 @@ corresponding HDF5 file is unpacked; only top-level `data_protocol` is removed
|
|
|
33
33
|
from that algorithm-facing projection.
|
|
34
34
|
|
|
35
35
|
Downloads use bounded parallelism, retries, and compatible partial-file resume.
|
|
36
|
+
Within a manifest, the first occurrence of a target path keeps its name; later
|
|
37
|
+
occurrences receive `-1`, `-2`, etc. before the final extension (for example,
|
|
38
|
+
`episode1.hdf5`, `episode1-1.hdf5`, `episode1-2.hdf5`). Numbering skips names and
|
|
39
|
+
paths already claimed by other entries, including partial files and metadata.
|
|
40
|
+
Different relative directories remain independent. Names are assigned in manifest
|
|
41
|
+
order, with Windows case-insensitive matching, before any downloads start.
|
|
42
|
+
Rerunning the same manifest preserves the assigned names for skipping and resume;
|
|
43
|
+
existing files on disk do not trigger new suffixes. This is not cross-batch name
|
|
44
|
+
allocation: changing the manifest entries or their order can change the mapping.
|
|
36
45
|
When `sha256` is present it is always checked before the final file is replaced.
|
|
37
46
|
TLS certificate verification is disabled by default for internal endpoints;
|
|
38
47
|
pass `--secure` to enable certificate verification.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.3"
|
|
@@ -74,7 +74,6 @@ class OutputRootPlan:
|
|
|
74
74
|
):
|
|
75
75
|
raise UnsafeOutputPathError("unsafe output path")
|
|
76
76
|
check_claim(path, self.root)
|
|
77
|
-
reject_existing_aliases(self.claims)
|
|
78
77
|
|
|
79
78
|
|
|
80
79
|
def safe_lstat(path: Path) -> os.stat_result | None:
|
|
@@ -140,24 +139,19 @@ def check_claim(path: Path, root: Path) -> None:
|
|
|
140
139
|
|
|
141
140
|
def reject_existing_aliases(claims: tuple[Path, ...]) -> None:
|
|
142
141
|
resolved: set[str] = set()
|
|
143
|
-
|
|
142
|
+
identities: set[tuple[int, int]] = set()
|
|
144
143
|
for claim in claims:
|
|
145
144
|
key = os.path.normcase(os.path.normpath(str(claim.resolve(strict=False))))
|
|
146
145
|
if key in resolved:
|
|
147
146
|
raise UnsafeOutputPathError("unsafe output path")
|
|
148
147
|
resolved.add(key)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
except UnsafeOutputPathError:
|
|
157
|
-
raise
|
|
158
|
-
except OSError as exc:
|
|
159
|
-
raise UnsafeOutputPathError("unsafe output path") from exc
|
|
160
|
-
existing.append(claim)
|
|
148
|
+
value = safe_lstat(claim)
|
|
149
|
+
if value is None:
|
|
150
|
+
continue
|
|
151
|
+
identity = value.st_dev, value.st_ino
|
|
152
|
+
if identity in identities:
|
|
153
|
+
raise UnsafeOutputPathError("unsafe output path")
|
|
154
|
+
identities.add(identity)
|
|
161
155
|
|
|
162
156
|
|
|
163
157
|
def build_output_plan(files: tuple[ManifestFile, ...], out: Path) -> OutputRootPlan:
|
|
@@ -4,7 +4,7 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
6
|
import sys
|
|
7
|
-
from dataclasses import dataclass
|
|
7
|
+
from dataclasses import dataclass, replace
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any
|
|
10
10
|
from urllib.parse import urlsplit, urlunsplit
|
|
@@ -199,18 +199,17 @@ def parse_manifest(text: str) -> tuple[ManifestFile, ...]:
|
|
|
199
199
|
raise ManifestError(f"files[{index}].file_name must be a non-empty string")
|
|
200
200
|
safe_name = safe_file_name(file_name, index=index)
|
|
201
201
|
duplicate_key = os.path.normcase(safe_name)
|
|
202
|
-
if duplicate_key in destinations:
|
|
203
|
-
raise ManifestError(f"files[{index}].file_name resolves to a duplicate target")
|
|
204
|
-
destinations.add(duplicate_key)
|
|
205
202
|
final_claim = claim_parts(safe_name)
|
|
206
203
|
part_claim = (*final_claim[:-1], f"{final_claim[-1]}.part")
|
|
207
204
|
instruction_claim = (*final_claim[:-1], f"{final_claim[-1]}.json")
|
|
208
|
-
if
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
205
|
+
if duplicate_key not in destinations:
|
|
206
|
+
if any(
|
|
207
|
+
paths_conflict(claim, existing) for claim in (final_claim, part_claim) for existing in output_claims
|
|
208
|
+
) or any(paths_conflict(instruction_claim, existing) for existing in metadata_claims):
|
|
209
|
+
raise ManifestError(f"files[{index}].file_name creates an ambiguous target collision")
|
|
210
|
+
destinations.add(duplicate_key)
|
|
211
|
+
output_claims.extend((final_claim, part_claim))
|
|
212
|
+
metadata_claims.append(instruction_claim)
|
|
214
213
|
files.append(
|
|
215
214
|
ManifestFile(
|
|
216
215
|
download_url=download_url,
|
|
@@ -225,6 +224,31 @@ def parse_manifest(text: str) -> tuple[ManifestFile, ...]:
|
|
|
225
224
|
meta_data=metadata(item, index=index),
|
|
226
225
|
)
|
|
227
226
|
)
|
|
227
|
+
# Reserve every original name before allocating suffixes, independent of disk
|
|
228
|
+
# contents and worker order so the same manifest can resume at the same paths.
|
|
229
|
+
next_suffix: dict[str, int] = {}
|
|
230
|
+
for index, file in enumerate(files):
|
|
231
|
+
key = os.path.normcase(file.file_name)
|
|
232
|
+
if key not in next_suffix:
|
|
233
|
+
next_suffix[key] = 1
|
|
234
|
+
continue
|
|
235
|
+
stem, extension = os.path.splitext(file.file_name)
|
|
236
|
+
suffix = next_suffix[key]
|
|
237
|
+
while True:
|
|
238
|
+
candidate = f"{stem}-{suffix}{extension}"
|
|
239
|
+
suffix += 1
|
|
240
|
+
final_claim = claim_parts(candidate)
|
|
241
|
+
part_claim = (*final_claim[:-1], f"{final_claim[-1]}.part")
|
|
242
|
+
instruction_claim = (*final_claim[:-1], f"{final_claim[-1]}.json")
|
|
243
|
+
if any(
|
|
244
|
+
paths_conflict(claim, existing) for claim in (final_claim, part_claim) for existing in output_claims
|
|
245
|
+
) or any(paths_conflict(instruction_claim, existing) for existing in metadata_claims):
|
|
246
|
+
continue
|
|
247
|
+
output_claims.extend((final_claim, part_claim))
|
|
248
|
+
metadata_claims.append(instruction_claim)
|
|
249
|
+
files[index] = replace(file, file_name=candidate)
|
|
250
|
+
next_suffix[key] = suffix
|
|
251
|
+
break
|
|
228
252
|
return tuple(files)
|
|
229
253
|
|
|
230
254
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|