simulo 0.26.0__tar.gz → 0.28.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.
- {simulo-0.26.0/src/simulo.egg-info → simulo-0.28.0}/PKG-INFO +15 -2
- {simulo-0.26.0 → simulo-0.28.0}/PYPI.md +13 -0
- {simulo-0.26.0 → simulo-0.28.0}/pyproject.toml +2 -2
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/_runner.py +15 -3
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/app.py +37 -1
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/asset_api.py +1 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/bundle.py +2 -1
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/cli.py +234 -59
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/credentials.py +134 -53
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/http.py +118 -3
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/install_samples.py +138 -4
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/packaging.py +10 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/preflight_api.py +9 -13
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/preflight_render.py +36 -11
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/submit_api.py +45 -28
- {simulo-0.26.0 → simulo-0.28.0/src/simulo.egg-info}/PKG-INFO +15 -2
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo.egg-info/requires.txt +1 -1
- {simulo-0.26.0 → simulo-0.28.0}/MANIFEST.in +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/setup.cfg +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/__init__.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/__init__.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/_entrypoint.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/_mounts.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/_secure_downloads.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/asset.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/asset_package.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/asset_pins.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/builtin_aliases.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/cancel_api.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/config.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/discovery.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/export_api.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/export_bundle.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/facades.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/identity_api.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/jobs_api.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/learning.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/login.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/mode.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/outputs.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/registry.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/runtime.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/runtime_display.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/seed_ref.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/stub.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/templates/__init__.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/templates/inference/app.py.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/templates/inference/simuloignore.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/templates/scenario/app.py.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/templates/scenario/simuloignore.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/templates/training/app.py.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/templates/training/simuloignore.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/view_fragment.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/view_session_api.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/_client/volume.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/callbacks.py +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo/py.typed +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo.egg-info/SOURCES.txt +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo.egg-info/dependency_links.txt +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/src/simulo.egg-info/entry_points.txt +0 -0
- {simulo-0.26.0 → simulo-0.28.0}/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.
|
|
3
|
+
Version: 0.28.0
|
|
4
4
|
Summary: Simulo SDK and CLI — define robotics simulation and training apps in Python and run them on the Simulo cloud.
|
|
5
5
|
Author-email: Simulo Team <team@simulo.ai>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
15
15
|
Classifier: Typing :: Typed
|
|
16
16
|
Requires-Python: >=3.11
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: simulo-interfaces<0.
|
|
18
|
+
Requires-Dist: simulo-interfaces<0.21,>=0.20.0
|
|
19
19
|
Requires-Dist: mcap<2,>=1.3
|
|
20
20
|
Requires-Dist: defusedxml>=0.7.1
|
|
21
21
|
Requires-Dist: usd-core<27,>=26.5
|
|
@@ -75,6 +75,19 @@ To download a curated collection of runnable projects, use `simulo install sampl
|
|
|
75
75
|
It creates `./simulo-samples` as a normal Git clone, so `cd simulo-samples` and
|
|
76
76
|
`cat README.md` open the sample index.
|
|
77
77
|
|
|
78
|
+
Choose a destination with `--directory` and a Git branch, tag, or commit with
|
|
79
|
+
`--ref`. You can use either option on its own or combine them:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
simulo install samples --directory robotics-examples --ref main
|
|
83
|
+
cd robotics-examples
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The destination must not exist, and its parent directory must already exist.
|
|
87
|
+
The directory appears only after the clone and requested checkout succeed.
|
|
88
|
+
Use a tag or full commit ID instead of `main` to select a fixed revision.
|
|
89
|
+
With `--ref`, the checkout is detached at the selected commit.
|
|
90
|
+
|
|
78
91
|
### Or write an app by hand
|
|
79
92
|
|
|
80
93
|
```python
|
|
@@ -35,6 +35,19 @@ To download a curated collection of runnable projects, use `simulo install sampl
|
|
|
35
35
|
It creates `./simulo-samples` as a normal Git clone, so `cd simulo-samples` and
|
|
36
36
|
`cat README.md` open the sample index.
|
|
37
37
|
|
|
38
|
+
Choose a destination with `--directory` and a Git branch, tag, or commit with
|
|
39
|
+
`--ref`. You can use either option on its own or combine them:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
simulo install samples --directory robotics-examples --ref main
|
|
43
|
+
cd robotics-examples
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The destination must not exist, and its parent directory must already exist.
|
|
47
|
+
The directory appears only after the clone and requested checkout succeed.
|
|
48
|
+
Use a tag or full commit ID instead of `main` to select a fixed revision.
|
|
49
|
+
With `--ref`, the checkout is detached at the selected commit.
|
|
50
|
+
|
|
38
51
|
### Or write an app by hand
|
|
39
52
|
|
|
40
53
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simulo"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.28.0"
|
|
8
8
|
description = "Simulo SDK and CLI — define robotics simulation and training apps in Python and run them on the Simulo cloud."
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -26,7 +26,7 @@ classifiers = [
|
|
|
26
26
|
# mcap: used by `simulo recordings` to read back a downloaded recording and
|
|
27
27
|
# verify it (message count) — MIT-licensed, deps only lz4+zstandard.
|
|
28
28
|
dependencies = [
|
|
29
|
-
"simulo-interfaces>=0.
|
|
29
|
+
"simulo-interfaces>=0.20.0,<0.21",
|
|
30
30
|
"mcap>=1.3,<2",
|
|
31
31
|
# Hardened XML parsing for URDF ingestion (USD Asset Catalogs, PR-12 fix
|
|
32
32
|
# loop 2, security NIT): stdlib xml.etree.ElementTree relies on
|
|
@@ -27,14 +27,26 @@ from typing import Any, NoReturn, Optional
|
|
|
27
27
|
|
|
28
28
|
from simulo.interfaces.ids import JobId, JobPublicId
|
|
29
29
|
from simulo.interfaces.platform.enums import JobStatus
|
|
30
|
-
from simulo.interfaces.platform.runs import JOB_SCOPE_MINE
|
|
30
|
+
from simulo.interfaces.platform.runs import JOB_SCOPE_MINE
|
|
31
31
|
|
|
32
32
|
#: Poll cadence for cloud ``.get()``. Module-level so tests can shrink it.
|
|
33
33
|
_POLL_INTERVAL_S = 1.0
|
|
34
34
|
|
|
35
|
-
_TERMINAL_STATUS_STRINGS = frozenset(str(status) for status in TERMINAL_JOB_STATUSES)
|
|
36
35
|
_COMPLETED_STATUS = str(JobStatus.COMPLETED)
|
|
37
36
|
|
|
37
|
+
#: The tolerant-client check (`M55-org-runtime-policy`, issue #1537):
|
|
38
|
+
#: EVERY status this client can observe outside these two live values is
|
|
39
|
+
#: terminal, whether or not it is one this client's own ``JobStatus`` happens
|
|
40
|
+
#: to know by name. Membership is checked as "not one of the known LIVE
|
|
41
|
+
#: values", never "is one of the known TERMINAL values" — a closed terminal
|
|
42
|
+
#: set silently treats a FUTURE value it has never heard of as still-live
|
|
43
|
+
#: and polls forever (:class:`JobTimeoutError` only fires when the caller
|
|
44
|
+
#: passed a ``timeout``; with none, :meth:`JobHandle._get_cloud` hangs).
|
|
45
|
+
#: Mirrors the existing precedent for an unrecognized ``kind``
|
|
46
|
+
#: (``simulo.interfaces.platform.submit``): a value outside the known set is
|
|
47
|
+
#: treated as data to render, never as grounds to keep waiting on it.
|
|
48
|
+
_LIVE_STATUS_STRINGS = frozenset({str(JobStatus.QUEUED), str(JobStatus.RUNNING)})
|
|
49
|
+
|
|
38
50
|
|
|
39
51
|
class JobFailedError(RuntimeError):
|
|
40
52
|
"""A cloud job reached a terminal ``failed``/``cancelled`` status.
|
|
@@ -154,7 +166,7 @@ class JobHandle:
|
|
|
154
166
|
while True:
|
|
155
167
|
record = client.get_job(str(self._job_id), scope=JOB_SCOPE_MINE)
|
|
156
168
|
status = str(record.get("status"))
|
|
157
|
-
if status in
|
|
169
|
+
if status not in _LIVE_STATUS_STRINGS:
|
|
158
170
|
if status == _COMPLETED_STATUS:
|
|
159
171
|
return client.get_result(str(self._job_id), scope=JOB_SCOPE_MINE)
|
|
160
172
|
exit_code = record.get("exit_code")
|
|
@@ -514,6 +514,26 @@ def _spawn_cloud(
|
|
|
514
514
|
# already lives there, keyed by this same `job_name`.
|
|
515
515
|
job_entry = manifest.get("jobs", {}).get(job_name) or {}
|
|
516
516
|
system = (job_entry.get("resources") or {}).get("system")
|
|
517
|
+
# The job's own manifest-declared `timeout` (`M55-org-runtime-policy`,
|
|
518
|
+
# issue #1537) — already validated at `@app.job(timeout=...)` decoration
|
|
519
|
+
# time and recorded on THIS job's own manifest entry, exactly like
|
|
520
|
+
# `system` just above. Forwarded explicitly to `create_job` because the
|
|
521
|
+
# control plane never parses the uploaded manifest archive itself (see
|
|
522
|
+
# `JobSubmitRequest.manifest_timeout_s`'s own docstring).
|
|
523
|
+
manifest_timeout_s = job_entry.get("timeout")
|
|
524
|
+
if manifest_timeout_s is not None and (
|
|
525
|
+
isinstance(manifest_timeout_s, bool) or not isinstance(manifest_timeout_s, int)
|
|
526
|
+
):
|
|
527
|
+
# This manifest was just written by THIS process (see the comment on
|
|
528
|
+
# `Manifest.from_json` re-parsing above) — a non-integer `timeout`
|
|
529
|
+
# here would mean `@app.job`'s own decoration-time validation
|
|
530
|
+
# somehow let one through, not an untrusted value to degrade
|
|
531
|
+
# silently on. Fail loudly rather than send a malformed wire field.
|
|
532
|
+
raise RuntimeError(
|
|
533
|
+
f"Package at {package_path} has a non-integer jobs[{job_name!r}].timeout "
|
|
534
|
+
f"({manifest_timeout_s!r}) in its manifest — this should be impossible; "
|
|
535
|
+
"please report this as a bug."
|
|
536
|
+
)
|
|
517
537
|
|
|
518
538
|
tar_path = ensure_tar(package_path)
|
|
519
539
|
archive_bytes = tar_path.read_bytes()
|
|
@@ -568,6 +588,7 @@ def _spawn_cloud(
|
|
|
568
588
|
assets=asset_pins,
|
|
569
589
|
app_name=app_name,
|
|
570
590
|
system=system,
|
|
591
|
+
timeout_s=manifest_timeout_s,
|
|
571
592
|
)
|
|
572
593
|
|
|
573
594
|
raw_public_id = record.get("public_id")
|
|
@@ -590,9 +611,24 @@ def _spawn_cloud(
|
|
|
590
611
|
# the ref resolved to the intended source job. Seed provenance still
|
|
591
612
|
# carries machine UUIDs only, so keep this confirmation neutral rather
|
|
592
613
|
# than leaking a UUID while that response contract catches up.
|
|
614
|
+
#
|
|
615
|
+
# `seed["name"]`/`seed["kind"]` are server-supplied text: a
|
|
616
|
+
# compromised control plane — or a user pointed at an attacker-run
|
|
617
|
+
# `SIMULO_API_URL` — could embed ANSI/OSC sequences here, so both
|
|
618
|
+
# route through the same module `cli.py`'s own sanitizers are built
|
|
619
|
+
# on (`preflight_render`) rather than a second one.
|
|
620
|
+
# `sanitize_server_text` alone is NOT enough here (cross-vendor
|
|
621
|
+
# review): it deliberately preserves TAB/LF for its usual
|
|
622
|
+
# multi-line preflight-finding callers, but this confirmation is a
|
|
623
|
+
# single line — an unstripped LF would forge an extra line this
|
|
624
|
+
# code never printed. `_single_line` collapses it instead.
|
|
625
|
+
from simulo._client.preflight_render import sanitize_server_text_single_line
|
|
626
|
+
|
|
593
627
|
seed = record.get("seed") or {}
|
|
628
|
+
seed_name = sanitize_server_text_single_line(str(seed.get("name")))
|
|
629
|
+
seed_kind = sanitize_server_text_single_line(str(seed.get("kind")))
|
|
594
630
|
print(
|
|
595
|
-
f"Continuing from selected job ({
|
|
631
|
+
f"Continuing from selected job ({seed_name}, {seed_kind} checkpoint)",
|
|
596
632
|
file=sys.stderr,
|
|
597
633
|
)
|
|
598
634
|
|
|
@@ -20,7 +20,7 @@ from pathlib import Path
|
|
|
20
20
|
from typing import TYPE_CHECKING, Any, Optional, Sequence
|
|
21
21
|
|
|
22
22
|
from simulo._client.asset import Asset
|
|
23
|
-
from simulo._client.packaging import canonical_args, package, read_bundle_package_id
|
|
23
|
+
from simulo._client.packaging import assert_credentials_outside_project, canonical_args, package, read_bundle_package_id
|
|
24
24
|
from simulo._client.volume import Volume
|
|
25
25
|
from simulo.interfaces.platform import is_platform_pinned
|
|
26
26
|
from simulo.interfaces.platform.manifest import Manifest
|
|
@@ -222,6 +222,7 @@ def ensure_bundle(
|
|
|
222
222
|
(deterministically restoring the correct content for this exact
|
|
223
223
|
submission), never served as a silently wrong package.
|
|
224
224
|
"""
|
|
225
|
+
assert_credentials_outside_project(project_root)
|
|
225
226
|
extra_asset_uris = tuple(sorted({asset.uri for asset in extra_assets}))
|
|
226
227
|
cache_key = (id(app), job_name, canonical_args(args), extra_asset_uris)
|
|
227
228
|
cached = _BUNDLE_CACHE.get(cache_key)
|