simulo 0.28.2__tar.gz → 0.28.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.
- {simulo-0.28.2/src/simulo.egg-info → simulo-0.28.3}/PKG-INFO +1 -1
- {simulo-0.28.2 → simulo-0.28.3}/pyproject.toml +1 -1
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/asset_package.py +6 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/cli.py +84 -5
- {simulo-0.28.2 → simulo-0.28.3/src/simulo.egg-info}/PKG-INFO +1 -1
- {simulo-0.28.2 → simulo-0.28.3}/MANIFEST.in +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/PYPI.md +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/setup.cfg +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/__init__.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/__init__.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/_entrypoint.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/_mounts.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/_runner.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/_secure_downloads.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/app.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/asset.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/asset_api.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/asset_pins.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/builtin_aliases.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/bundle.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/cancel_api.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/config.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/credentials.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/discovery.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/export_api.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/export_bundle.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/facades.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/http.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/identity_api.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/install_samples.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/jobs_api.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/learning.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/login.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/mode.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/outputs.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/packaging.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/preflight_api.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/preflight_render.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/registry.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/runtime.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/runtime_display.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/seed_ref.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/stub.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/submit_api.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/templates/__init__.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/templates/inference/app.py.tmpl +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/templates/inference/simuloignore.tmpl +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/templates/scenario/app.py.tmpl +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/templates/scenario/simuloignore.tmpl +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/templates/training/app.py.tmpl +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/templates/training/simuloignore.tmpl +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/view_fragment.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/view_session_api.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/_client/volume.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/callbacks.py +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo/py.typed +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo.egg-info/SOURCES.txt +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo.egg-info/dependency_links.txt +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo.egg-info/entry_points.txt +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo.egg-info/requires.txt +0 -0
- {simulo-0.28.2 → simulo-0.28.3}/src/simulo.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simulo
|
|
3
|
-
Version: 0.28.
|
|
3
|
+
Version: 0.28.3
|
|
4
4
|
Summary: Python SDK and CLI for cloud-native robotics simulation, reinforcement learning, and robot policy training on managed GPUs.
|
|
5
5
|
Author-email: Simulo Team <team@simulo.ai>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simulo"
|
|
7
|
-
version = "0.28.
|
|
7
|
+
version = "0.28.3"
|
|
8
8
|
description = "Python SDK and CLI for cloud-native robotics simulation, reinforcement learning, and robot policy training on managed GPUs."
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -82,6 +82,10 @@ from simulo.interfaces.platform.asset_catalog import (
|
|
|
82
82
|
#: recipe can never silently collide with today's identities.
|
|
83
83
|
_CONTENT_DIGEST_SCHEMA = "simulo.asset_content.v1"
|
|
84
84
|
|
|
85
|
+
#: Local-cache metadata written after extraction. Like the backend validator,
|
|
86
|
+
#: the packager excludes this exact name only at the package root.
|
|
87
|
+
_ASSET_CACHE_SIDECAR_FILENAME = "simulo.asset.json"
|
|
88
|
+
|
|
85
89
|
#: USD layer suffixes the inspector parses for references. ``.usdz`` is a
|
|
86
90
|
#: sealed package — included as an opaque file, valid as an entry only when
|
|
87
91
|
#: named explicitly via ``--entry`` (never auto-detected).
|
|
@@ -516,6 +520,8 @@ def _collect_tree_files(root: Path) -> list[tuple[str, Path]]:
|
|
|
516
520
|
if abspath.is_symlink():
|
|
517
521
|
continue
|
|
518
522
|
rel = PurePosixPath(abspath.relative_to(root).as_posix())
|
|
523
|
+
if str(rel) == _ASSET_CACHE_SIDECAR_FILENAME:
|
|
524
|
+
continue
|
|
519
525
|
collected.append((str(rel), abspath))
|
|
520
526
|
collected.sort(key=lambda item: item[0])
|
|
521
527
|
return collected
|
|
@@ -7222,6 +7222,11 @@ def _cmd_systems(args: argparse.Namespace) -> int:
|
|
|
7222
7222
|
#: not by any prose document.
|
|
7223
7223
|
ASSET_CACHE_ROOT = Path.home() / ".simulo" / "assets"
|
|
7224
7224
|
|
|
7225
|
+
#: Sidecar consumed by ``simulo-backend`` when a cached asset path names a
|
|
7226
|
+
#: directory rather than the loadable file inside it. Kept private because the
|
|
7227
|
+
#: thin client must not import the backend package just to share this filename.
|
|
7228
|
+
_ASSET_SIDECAR_FILENAME = "simulo.asset.json"
|
|
7229
|
+
|
|
7225
7230
|
#: Conservative Windows ``MAX_PATH`` guard (260 chars, minus 1) applied to
|
|
7226
7231
|
#: every extracted cache path — a defensive REFUSAL, not a guarantee: a
|
|
7227
7232
|
#: legitimately deep USD tree can still need Windows long-path support
|
|
@@ -7350,7 +7355,7 @@ def _validate_asset_archive_member(member: tarfile.TarInfo, dest_root: Path) ->
|
|
|
7350
7355
|
return resolved, None
|
|
7351
7356
|
|
|
7352
7357
|
|
|
7353
|
-
def _extract_asset_archive(tar_path: Path, dest_dir: Path) ->
|
|
7358
|
+
def _extract_asset_archive(tar_path: Path, dest_dir: Path, *, entry_path: Optional[str] = None) -> Optional[str]:
|
|
7354
7359
|
"""Safely extract *tar_path* (a digest-verified asset archive) into
|
|
7355
7360
|
*dest_dir* — member-by-member, never ``TarFile.extractall``. This
|
|
7356
7361
|
function IS the safety filter ``extractall``'s ``filter=`` parameter
|
|
@@ -7363,8 +7368,12 @@ def _extract_asset_archive(tar_path: Path, dest_dir: Path) -> None:
|
|
|
7363
7368
|
single unsafe member aborts the whole extraction with nothing written,
|
|
7364
7369
|
never a partial mix of safe and unsafe content.
|
|
7365
7370
|
|
|
7366
|
-
|
|
7367
|
-
|
|
7371
|
+
When *entry_path* is provided, it must name one of the archive's regular
|
|
7372
|
+
files through the same containment check as every member. The normalized
|
|
7373
|
+
package-relative path is returned for the platform sidecar.
|
|
7374
|
+
|
|
7375
|
+
Raises :class:`AssetExtractionError` on any unsafe/malformed member or
|
|
7376
|
+
entry, a non-tar file, or a local filesystem failure.
|
|
7368
7377
|
"""
|
|
7369
7378
|
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
7370
7379
|
dest_root = dest_dir.resolve()
|
|
@@ -7378,6 +7387,20 @@ def _extract_asset_archive(tar_path: Path, dest_dir: Path) -> None:
|
|
|
7378
7387
|
f"Refusing to extract {member.name!r}: {reason}. Nothing was written for this asset."
|
|
7379
7388
|
)
|
|
7380
7389
|
planned.append((member, target))
|
|
7390
|
+
entry_target: Optional[Path] = None
|
|
7391
|
+
if entry_path is not None:
|
|
7392
|
+
entry_member = tarfile.TarInfo(entry_path)
|
|
7393
|
+
entry_member.type = tarfile.REGTYPE
|
|
7394
|
+
entry_target, reason = _validate_asset_archive_member(entry_member, dest_root)
|
|
7395
|
+
if entry_target is None:
|
|
7396
|
+
raise AssetExtractionError(
|
|
7397
|
+
f"Refusing asset entry path {entry_path!r}: {reason}. Nothing was written for this asset."
|
|
7398
|
+
)
|
|
7399
|
+
if not any(member.isfile() and target == entry_target for member, target in planned):
|
|
7400
|
+
raise AssetExtractionError(
|
|
7401
|
+
f"Refusing asset entry path {entry_path!r}: it does not name a regular file in the "
|
|
7402
|
+
"downloaded archive. Nothing was written for this asset."
|
|
7403
|
+
)
|
|
7381
7404
|
for member, target in planned:
|
|
7382
7405
|
if member.isdir():
|
|
7383
7406
|
target.mkdir(parents=True, exist_ok=True)
|
|
@@ -7388,10 +7411,50 @@ def _extract_asset_archive(tar_path: Path, dest_dir: Path) -> None:
|
|
|
7388
7411
|
raise AssetExtractionError(f"Archive member {member.name!r} has no readable content.")
|
|
7389
7412
|
with src, open(target, "wb") as dst:
|
|
7390
7413
|
shutil.copyfileobj(src, dst)
|
|
7414
|
+
if entry_target is not None:
|
|
7415
|
+
return entry_target.relative_to(dest_root).as_posix()
|
|
7391
7416
|
except tarfile.TarError as exc:
|
|
7392
7417
|
raise AssetExtractionError(f"The downloaded archive is not a valid tar file: {exc}") from exc
|
|
7393
7418
|
except OSError as exc:
|
|
7394
7419
|
raise AssetExtractionError(f"Could not write the extracted asset to {dest_dir}: {exc}") from exc
|
|
7420
|
+
return None
|
|
7421
|
+
|
|
7422
|
+
|
|
7423
|
+
def _write_asset_cache_sidecar(dest_dir: Path, *, uri: str, entry: str, kind: str, archive_sha256: str) -> None:
|
|
7424
|
+
"""Write the backend-compatible entry record after trusted extraction."""
|
|
7425
|
+
sidecar = {
|
|
7426
|
+
"uri": uri,
|
|
7427
|
+
"entry": entry,
|
|
7428
|
+
"kind": kind,
|
|
7429
|
+
"digest": f"sha256:{archive_sha256}",
|
|
7430
|
+
}
|
|
7431
|
+
temp_path: Optional[Path] = None
|
|
7432
|
+
try:
|
|
7433
|
+
dest_root = dest_dir.resolve()
|
|
7434
|
+
with tempfile.NamedTemporaryFile(
|
|
7435
|
+
mode="w",
|
|
7436
|
+
encoding="utf-8",
|
|
7437
|
+
dir=dest_root,
|
|
7438
|
+
prefix=f".{_ASSET_SIDECAR_FILENAME}.",
|
|
7439
|
+
delete=False,
|
|
7440
|
+
) as temp_file:
|
|
7441
|
+
temp_path = Path(temp_file.name)
|
|
7442
|
+
temp_file.write(json.dumps(sidecar, indent=2) + "\n")
|
|
7443
|
+
# NamedTemporaryFile creates the file owner-only (0600). Give the
|
|
7444
|
+
# record the permissions a plain write would get, so anything that
|
|
7445
|
+
# reads the cache as another user keeps working.
|
|
7446
|
+
if hasattr(os, "fchmod"):
|
|
7447
|
+
current_umask = os.umask(0)
|
|
7448
|
+
os.umask(current_umask)
|
|
7449
|
+
os.fchmod(temp_file.fileno(), 0o666 & ~current_umask)
|
|
7450
|
+
os.replace(temp_path, dest_root / _ASSET_SIDECAR_FILENAME)
|
|
7451
|
+
except OSError as exc:
|
|
7452
|
+
if temp_path is not None:
|
|
7453
|
+
try:
|
|
7454
|
+
temp_path.unlink(missing_ok=True)
|
|
7455
|
+
except OSError:
|
|
7456
|
+
pass
|
|
7457
|
+
raise AssetExtractionError(f"Could not write the asset entry record in {dest_dir}: {exc}") from exc
|
|
7395
7458
|
|
|
7396
7459
|
|
|
7397
7460
|
#: Statuses an ``AssetVersionSummary`` can carry while it is still mid-pipeline
|
|
@@ -8004,6 +8067,13 @@ def _asset_get_impl(args: argparse.Namespace) -> int:
|
|
|
8004
8067
|
expected_sha256 = str(target.get("archive_sha256") or "").lower().removeprefix("sha256:")
|
|
8005
8068
|
display_ref = f"{parsed.kind}/{parsed.name}:v{version_number}"
|
|
8006
8069
|
|
|
8070
|
+
if not entry_path:
|
|
8071
|
+
raise AssetExtractionError(f"{display_ref} has no loadable entry path. Nothing was written.")
|
|
8072
|
+
if "\x00" in entry_path:
|
|
8073
|
+
raise AssetExtractionError(
|
|
8074
|
+
f"Refusing asset entry path {entry_path!r}: NUL characters are refused. Nothing was written for this asset."
|
|
8075
|
+
)
|
|
8076
|
+
|
|
8007
8077
|
# SECURITY (LOW, PR-8 trio review): a downloadable version with NO
|
|
8008
8078
|
# recorded digest is refused outright, before any network call — the
|
|
8009
8079
|
# earlier code treated a missing `archive_sha256` as "skip verification"
|
|
@@ -8038,9 +8108,18 @@ def _asset_get_impl(args: argparse.Namespace) -> int:
|
|
|
8038
8108
|
with tempfile.TemporaryDirectory(prefix="simulo-asset-get-") as tmp_dir:
|
|
8039
8109
|
tar_path = Path(tmp_dir) / "package.tar"
|
|
8040
8110
|
tar_path.write_bytes(data)
|
|
8041
|
-
_extract_asset_archive(tar_path, dest_dir)
|
|
8111
|
+
safe_entry_path = _extract_asset_archive(tar_path, dest_dir, entry_path=entry_path)
|
|
8112
|
+
if safe_entry_path is None:
|
|
8113
|
+
raise AssetExtractionError(f"{display_ref} has no loadable entry path. Nothing was written.")
|
|
8114
|
+
_write_asset_cache_sidecar(
|
|
8115
|
+
dest_dir,
|
|
8116
|
+
uri=f"{publisher}/{display_ref}",
|
|
8117
|
+
entry=safe_entry_path,
|
|
8118
|
+
kind=parsed.kind,
|
|
8119
|
+
archive_sha256=actual_sha256,
|
|
8120
|
+
)
|
|
8042
8121
|
|
|
8043
|
-
echo(f"Downloaded to {dest_dir}/ (entry: {entry_path
|
|
8122
|
+
echo(f"Downloaded to {dest_dir}/ (entry: {sanitize_server_text(entry_path)})")
|
|
8044
8123
|
if args.json:
|
|
8045
8124
|
print(
|
|
8046
8125
|
json.dumps(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simulo
|
|
3
|
-
Version: 0.28.
|
|
3
|
+
Version: 0.28.3
|
|
4
4
|
Summary: Python SDK and CLI for cloud-native robotics simulation, reinforcement learning, and robot policy training on managed GPUs.
|
|
5
5
|
Author-email: Simulo Team <team@simulo.ai>
|
|
6
6
|
License: BSD-3-Clause
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|