simulo 0.31.2__tar.gz → 0.32.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.31.2/src/simulo.egg-info → simulo-0.32.0}/PKG-INFO +5 -5
- {simulo-0.31.2 → simulo-0.32.0}/PYPI.md +3 -3
- {simulo-0.31.2 → simulo-0.32.0}/pyproject.toml +2 -2
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/__init__.py +2 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/_runner.py +7 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/app.py +34 -8
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/bundle.py +25 -18
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/cli.py +313 -74
- simulo-0.32.0/src/simulo/_client/evaluation_report.py +581 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/facades.py +52 -4
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/jobs_api.py +43 -17
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/learning.py +48 -6
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/packaging.py +43 -21
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/policies_api.py +12 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/preview_report.py +60 -22
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/submit_api.py +51 -4
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/templates/__init__.py +3 -3
- simulo-0.32.0/src/simulo/_client/templates/training/eval.py.tmpl +34 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/templates/training/task.py.tmpl +23 -17
- {simulo-0.31.2 → simulo-0.32.0/src/simulo.egg-info}/PKG-INFO +5 -5
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo.egg-info/SOURCES.txt +2 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo.egg-info/requires.txt +1 -1
- {simulo-0.31.2 → simulo-0.32.0}/MANIFEST.in +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/setup.cfg +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/__init__.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/_arguments.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/_mounts.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/_secure_downloads.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/asset.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/asset_api.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/asset_package.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/asset_pins.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/builtin_aliases.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/cancel_api.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/config.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/credentials.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/discovery.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/export_api.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/http.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/identity_api.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/install_samples.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/login.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/mode.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/onnx_bundle.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/outputs.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/preflight_api.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/preflight_render.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/registry.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/runtime.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/runtime_display.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/stub.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/templates/training/play.py.tmpl +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/templates/training/preview.py.tmpl +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/templates/training/simuloignore.tmpl +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/templates/training/train.py.tmpl +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/view_fragment.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/view_session_api.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/_client/volume.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/callbacks.py +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo/py.typed +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo.egg-info/dependency_links.txt +0 -0
- {simulo-0.31.2 → simulo-0.32.0}/src/simulo.egg-info/entry_points.txt +0 -0
- {simulo-0.31.2 → simulo-0.32.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.32.0
|
|
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
|
|
@@ -31,7 +31,7 @@ Classifier: Topic :: System :: Distributed Computing
|
|
|
31
31
|
Classifier: Typing :: Typed
|
|
32
32
|
Requires-Python: >=3.11
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
|
-
Requires-Dist: simulo-interfaces<0.
|
|
34
|
+
Requires-Dist: simulo-interfaces<0.24,>=0.23.0
|
|
35
35
|
Requires-Dist: mcap<2,>=1.3
|
|
36
36
|
Requires-Dist: defusedxml>=0.7.1
|
|
37
37
|
Requires-Dist: usd-core<27,>=26.5
|
|
@@ -115,7 +115,7 @@ See the repository README for setup and usage.
|
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
117
|
simulo jobs # list submitted jobs and status
|
|
118
|
-
simulo logs --follow # stream
|
|
118
|
+
simulo logs --follow # stream logs from your most recent job you submitted (train, play, preview or eval)
|
|
119
119
|
simulo result # read the job's result
|
|
120
120
|
simulo policy list # list training policies and checkpoints
|
|
121
121
|
simulo policy get policy_x:best # download a verified checkpoint
|
|
@@ -126,8 +126,8 @@ simulo view # view a running --viewstream job in 3D
|
|
|
126
126
|
simulo cancel # stop a queued or running job
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
Commands that accept a job ID default to the latest
|
|
130
|
-
|
|
129
|
+
Commands that accept a job ID default to the latest user-submitted job
|
|
130
|
+
(`train`, `play`, `preview`, or `eval`) and tell you its ID and type.
|
|
131
131
|
|
|
132
132
|
## Use versioned robot assets
|
|
133
133
|
|
|
@@ -59,7 +59,7 @@ See the repository README for setup and usage.
|
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
simulo jobs # list submitted jobs and status
|
|
62
|
-
simulo logs --follow # stream
|
|
62
|
+
simulo logs --follow # stream logs from your most recent job you submitted (train, play, preview or eval)
|
|
63
63
|
simulo result # read the job's result
|
|
64
64
|
simulo policy list # list training policies and checkpoints
|
|
65
65
|
simulo policy get policy_x:best # download a verified checkpoint
|
|
@@ -70,8 +70,8 @@ simulo view # view a running --viewstream job in 3D
|
|
|
70
70
|
simulo cancel # stop a queued or running job
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
Commands that accept a job ID default to the latest
|
|
74
|
-
|
|
73
|
+
Commands that accept a job ID default to the latest user-submitted job
|
|
74
|
+
(`train`, `play`, `preview`, or `eval`) and tell you its ID and type.
|
|
75
75
|
|
|
76
76
|
## Use versioned robot assets
|
|
77
77
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simulo"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.32.0"
|
|
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"
|
|
@@ -59,7 +59,7 @@ classifiers = [
|
|
|
59
59
|
# mcap: used by `simulo recordings` to read back a downloaded recording and
|
|
60
60
|
# verify it (message count) — MIT-licensed, deps only lz4+zstandard.
|
|
61
61
|
dependencies = [
|
|
62
|
-
"simulo-interfaces>=0.
|
|
62
|
+
"simulo-interfaces>=0.23.0,<0.24",
|
|
63
63
|
"mcap>=1.3,<2",
|
|
64
64
|
# Hardened XML parsing for URDF ingestion (USD Asset Catalogs, PR-12 fix
|
|
65
65
|
# loop 2, security NIT): stdlib xml.etree.ElementTree relies on
|
|
@@ -277,6 +277,7 @@ __all__ = [
|
|
|
277
277
|
"Trainer",
|
|
278
278
|
"Visual",
|
|
279
279
|
"World",
|
|
280
|
+
"evaluate",
|
|
280
281
|
"preview",
|
|
281
282
|
"save_output",
|
|
282
283
|
]
|
|
@@ -381,6 +382,7 @@ if TYPE_CHECKING:
|
|
|
381
382
|
from simulo._client.facades import RLPlayer as RLPlayer
|
|
382
383
|
from simulo._client.facades import RLTrainer as RLTrainer
|
|
383
384
|
from simulo._client.facades import Robot as Robot
|
|
385
|
+
from simulo._client.facades import evaluate as evaluate
|
|
384
386
|
from simulo._client.facades import preview as preview
|
|
385
387
|
from simulo.interfaces.authoring import SceneProtocol
|
|
386
388
|
from simulo.interfaces.runtime import (
|
|
@@ -92,6 +92,7 @@ class JobHandle:
|
|
|
92
92
|
token: Optional[str] = None,
|
|
93
93
|
job_type: Optional[JobType] = None,
|
|
94
94
|
input_checkpoint: Optional[str] = None,
|
|
95
|
+
compare_checkpoint: Optional[str] = None,
|
|
95
96
|
deferred_stdout: str = "",
|
|
96
97
|
deferred_stderr: str = "",
|
|
97
98
|
) -> None:
|
|
@@ -104,6 +105,7 @@ class JobHandle:
|
|
|
104
105
|
self._token = token
|
|
105
106
|
self._job_type = job_type
|
|
106
107
|
self._input_checkpoint = input_checkpoint
|
|
108
|
+
self._compare_checkpoint = compare_checkpoint
|
|
107
109
|
self._deferred_stdout = deferred_stdout
|
|
108
110
|
self._deferred_stderr = deferred_stderr
|
|
109
111
|
|
|
@@ -152,6 +154,11 @@ class JobHandle:
|
|
|
152
154
|
"""Policy checkpoint requested with ``simulo run --from``."""
|
|
153
155
|
return self._input_checkpoint
|
|
154
156
|
|
|
157
|
+
@property
|
|
158
|
+
def compare_checkpoint(self) -> Optional[str]:
|
|
159
|
+
"""Policy checkpoint requested with ``simulo run --compare``."""
|
|
160
|
+
return self._compare_checkpoint
|
|
161
|
+
|
|
155
162
|
@property
|
|
156
163
|
def deferred_stdout(self) -> str:
|
|
157
164
|
"""Submit-time output held until the cloud job header is printed."""
|
|
@@ -160,6 +160,7 @@ def viewstream_requested(enabled: bool) -> Iterator[None]:
|
|
|
160
160
|
|
|
161
161
|
_INPUT_CHECKPOINT_REF: Optional[str] = None
|
|
162
162
|
_INPUT_CHECKPOINT_FLAG = "--from"
|
|
163
|
+
_COMPARE_CHECKPOINT_REF: Optional[str] = None
|
|
163
164
|
|
|
164
165
|
|
|
165
166
|
class InputCheckpointRequiresCloudError(RuntimeError):
|
|
@@ -188,6 +189,18 @@ def input_checkpoint_requested(checkpoint_ref: Optional[str], *, flag: str = "--
|
|
|
188
189
|
_INPUT_CHECKPOINT_FLAG = previous_flag
|
|
189
190
|
|
|
190
191
|
|
|
192
|
+
@contextlib.contextmanager
|
|
193
|
+
def compare_checkpoint_requested(checkpoint_ref: Optional[str]) -> Iterator[None]:
|
|
194
|
+
"""Scope ``--compare``'s second checkpoint for one ``simulo run`` submit."""
|
|
195
|
+
global _COMPARE_CHECKPOINT_REF
|
|
196
|
+
previous = _COMPARE_CHECKPOINT_REF
|
|
197
|
+
_COMPARE_CHECKPOINT_REF = checkpoint_ref
|
|
198
|
+
try:
|
|
199
|
+
yield
|
|
200
|
+
finally:
|
|
201
|
+
_COMPARE_CHECKPOINT_REF = previous
|
|
202
|
+
|
|
203
|
+
|
|
191
204
|
# ---------------------------------------------------------------------------
|
|
192
205
|
# Asset-pin ambient signals — `simulo run --frozen` / `--strict-assets` scope
|
|
193
206
|
# these for one submit, exactly like `--viewstream` / `--from` above and for the
|
|
@@ -302,6 +315,17 @@ def _require_no_input_checkpoint_for_local_submit(reason: str) -> None:
|
|
|
302
315
|
)
|
|
303
316
|
|
|
304
317
|
|
|
318
|
+
def _require_no_compare_checkpoint_for_local_submit(reason: str) -> None:
|
|
319
|
+
"""Refuse a local-disk submit that would drop ``--compare``."""
|
|
320
|
+
if _COMPARE_CHECKPOINT_REF is None:
|
|
321
|
+
return
|
|
322
|
+
raise InputCheckpointRequiresCloudError(
|
|
323
|
+
f"--compare {_COMPARE_CHECKPOINT_REF!r} needs a cloud control plane to resolve the checkpoint, but {reason}. "
|
|
324
|
+
"A local-disk submit only writes the package and cannot compare policy checkpoints. Log in first "
|
|
325
|
+
"(`simulo login`) or point SIMULO_API_URL/SIMULO_ENV at a control plane (and unset SIMULO_SUBMIT=local)."
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
|
|
305
329
|
@dataclass
|
|
306
330
|
class JobSpec:
|
|
307
331
|
"""Recorded metadata for one ``@app.job``-decorated function."""
|
|
@@ -491,6 +515,7 @@ def _spawn_cloud(
|
|
|
491
515
|
*,
|
|
492
516
|
viewstream: bool = False,
|
|
493
517
|
input_checkpoint: Optional[str] = None,
|
|
518
|
+
compare_checkpoint: Optional[str] = None,
|
|
494
519
|
frozen_assets: bool = False,
|
|
495
520
|
strict_assets: bool = False,
|
|
496
521
|
skip_preflight: bool = False,
|
|
@@ -683,6 +708,7 @@ def _spawn_cloud(
|
|
|
683
708
|
args=args,
|
|
684
709
|
viewstream=viewstream,
|
|
685
710
|
input_checkpoint=input_checkpoint,
|
|
711
|
+
compare_checkpoint=compare_checkpoint,
|
|
686
712
|
assets=asset_pins,
|
|
687
713
|
app_name=app_name,
|
|
688
714
|
system=system,
|
|
@@ -716,6 +742,7 @@ def _spawn_cloud(
|
|
|
716
742
|
token=token,
|
|
717
743
|
job_type=job_type,
|
|
718
744
|
input_checkpoint=input_checkpoint,
|
|
745
|
+
compare_checkpoint=compare_checkpoint,
|
|
719
746
|
deferred_stdout=deferred_stdout,
|
|
720
747
|
deferred_stderr=deferred_stderr,
|
|
721
748
|
)
|
|
@@ -807,6 +834,7 @@ class JobFunction:
|
|
|
807
834
|
# local-disk submit must never depend on the network succeeding.
|
|
808
835
|
if config.is_submit_forced_local():
|
|
809
836
|
_require_no_input_checkpoint_for_local_submit("SIMULO_SUBMIT=local forces a local-disk submit")
|
|
837
|
+
_require_no_compare_checkpoint_for_local_submit("SIMULO_SUBMIT=local forces a local-disk submit")
|
|
810
838
|
handle: JobHandle = _spawn_local(self._spec.name, package_path)
|
|
811
839
|
else:
|
|
812
840
|
creds = credentials.try_get_valid_credentials()
|
|
@@ -819,6 +847,7 @@ class JobFunction:
|
|
|
819
847
|
creds,
|
|
820
848
|
viewstream=_VIEWSTREAM_REQUESTED,
|
|
821
849
|
input_checkpoint=_INPUT_CHECKPOINT_REF,
|
|
850
|
+
compare_checkpoint=_COMPARE_CHECKPOINT_REF,
|
|
822
851
|
frozen_assets=_FROZEN_ASSETS_REQUESTED,
|
|
823
852
|
strict_assets=_STRICT_ASSETS_REQUESTED,
|
|
824
853
|
skip_preflight=_SKIP_PREFLIGHT_REQUESTED,
|
|
@@ -850,6 +879,7 @@ class JobFunction:
|
|
|
850
879
|
)
|
|
851
880
|
else:
|
|
852
881
|
_require_no_input_checkpoint_for_local_submit("no cloud target is configured")
|
|
882
|
+
_require_no_compare_checkpoint_for_local_submit("no cloud target is configured")
|
|
853
883
|
handle = _spawn_local(self._spec.name, package_path)
|
|
854
884
|
_record_spawn(handle)
|
|
855
885
|
return handle
|
|
@@ -1430,11 +1460,11 @@ class App:
|
|
|
1430
1460
|
raise AttributeError(f"{type(self).__name__!s} object has no attribute {name!r}")
|
|
1431
1461
|
|
|
1432
1462
|
def success(self, fn: F) -> F:
|
|
1433
|
-
"""Register an evaluation success
|
|
1463
|
+
"""Register an evaluation success rule.
|
|
1434
1464
|
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1465
|
+
The client only records the function, so discovery can enforce that an
|
|
1466
|
+
eval file has exactly one and every other job type has none; the
|
|
1467
|
+
evaluation job calls it on each episode's last step.
|
|
1438
1468
|
"""
|
|
1439
1469
|
self._success_functions.append(fn)
|
|
1440
1470
|
return fn
|
|
@@ -1449,8 +1479,6 @@ class App:
|
|
|
1449
1479
|
spec = self._jobs[job_name]
|
|
1450
1480
|
where = location or f"app {self.name!r}"
|
|
1451
1481
|
successes = self.success_functions
|
|
1452
|
-
if spec.job_type is JobType.EVAL and not successes:
|
|
1453
|
-
raise JobDeclarationError("evaluation isn't available yet. Use a train job for now.")
|
|
1454
1482
|
if spec.job_type is JobType.EVAL:
|
|
1455
1483
|
if len(successes) != 1:
|
|
1456
1484
|
names = ", ".join(fn.__name__ for fn in successes) or "none"
|
|
@@ -1474,8 +1502,6 @@ class App:
|
|
|
1474
1502
|
"but a preview job takes no policy. Remove it; a policy goes to a play or eval file "
|
|
1475
1503
|
"with --policy."
|
|
1476
1504
|
)
|
|
1477
|
-
if spec.job_type is JobType.EVAL:
|
|
1478
|
-
raise JobDeclarationError("evaluation isn't available yet. Use a train job for now.")
|
|
1479
1505
|
if spec.job_type is JobType.ASSET_VALIDATION:
|
|
1480
1506
|
raise JobDeclarationError("Asset validation runs when you publish an asset: simulo asset publish.")
|
|
1481
1507
|
if spec.job_type is JobType.EXPORT:
|
|
@@ -4,9 +4,9 @@ Shared by ``simulo run`` (the CLI) and ``JobFunction.spawn`` — the *submit* pa
|
|
|
4
4
|
discover the app's metadata and write the package (manifest + source bundle) to
|
|
5
5
|
disk. The manifest builder reads only the public ``App`` surface, so it works in
|
|
6
6
|
discovery mode (where submit runs). ``ensure_bundle`` memoises the written package
|
|
7
|
-
per *submission* — ``(app, job_name, args)`` — so re-submitting the
|
|
8
|
-
the same args reuses the package, while a second spawn with
|
|
9
|
-
different args writes a distinct package (no clobber).
|
|
7
|
+
per *submission* — ``(app, entry_file, job_name, args)`` — so re-submitting the
|
|
8
|
+
same file and job with the same args reuses the package, while a second spawn with
|
|
9
|
+
a different entry file, job, or args writes a distinct package (no clobber).
|
|
10
10
|
|
|
11
11
|
Submit NEVER executes a job and NEVER invokes the backend — it only writes the
|
|
12
12
|
package. This module imports no heavy library; it stays on the torch-free side of
|
|
@@ -16,11 +16,17 @@ package it writes.
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from pathlib import Path
|
|
19
|
+
from pathlib import Path, PurePosixPath
|
|
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
|
|
23
|
+
from simulo._client.packaging import (
|
|
24
|
+
assert_credentials_outside_project,
|
|
25
|
+
canonical_args,
|
|
26
|
+
package,
|
|
27
|
+
project_relative_entry_file,
|
|
28
|
+
read_bundle_package_id,
|
|
29
|
+
)
|
|
24
30
|
from simulo._client.volume import Volume
|
|
25
31
|
from simulo.interfaces.platform import is_platform_pinned
|
|
26
32
|
from simulo.interfaces.platform.manifest import Manifest
|
|
@@ -33,10 +39,10 @@ if TYPE_CHECKING: # typing-only; avoids any import cycle with app.py at runtime
|
|
|
33
39
|
DEFAULT_OUT_SUBPATH = (".simulo", "packages")
|
|
34
40
|
|
|
35
41
|
# Memoise the built bundle per *submission* (per process). Keyed on
|
|
36
|
-
# ``(id(app), job_name, canonical_json(args))`` — an App lives for
|
|
37
|
-
# so identity reuse is not a concern, and keying on the
|
|
38
|
-
#
|
|
39
|
-
# reused for all.
|
|
42
|
+
# ``(id(app), entry_file, job_name, canonical_json(args))`` — an App lives for
|
|
43
|
+
# the whole run, so identity reuse is not a concern, and keying on the selected
|
|
44
|
+
# file + job + args means multiple programmatic spawns each write their own
|
|
45
|
+
# package instead of the first one being reused for all.
|
|
40
46
|
#
|
|
41
47
|
# Each entry stores ``(bundle_dir, package_id)`` — the package_id the bundle
|
|
42
48
|
# was written for when THIS process cached it — not just the path. The local
|
|
@@ -48,15 +54,15 @@ DEFAULT_OUT_SUBPATH = (".simulo", "packages")
|
|
|
48
54
|
# rmtree+rewrites whatever it finds there). ``ensure_bundle`` re-verifies the
|
|
49
55
|
# manifest on every cache hit before trusting it (see below) — turning a
|
|
50
56
|
# prefix collision into a redundant rebuild, never a silently wrong package.
|
|
51
|
-
#: Cache key's
|
|
57
|
+
#: Cache key's 5th element is the sorted, de-duplicated URIs of every
|
|
52
58
|
#: ambiently-CAPTURED asset visible at this ``ensure_bundle`` call (construction-
|
|
53
59
|
#: capture — see ``asset.py``) — included so a LATER spawn() in the same process,
|
|
54
60
|
#: after user code has constructed MORE assets since an earlier spawn() with
|
|
55
|
-
#: the identical ``(app, job_name, args)``, is never served a stale cached bundle
|
|
61
|
+
#: the identical ``(app, entry_file, job_name, args)``, is never served a stale cached bundle
|
|
56
62
|
#: whose ``resources`` predates those constructions (``package_id`` itself never
|
|
57
63
|
#: changes with the asset set — see the package-hash-invariance rule — so the
|
|
58
64
|
#: `package_id` re-check below cannot by itself catch this staleness).
|
|
59
|
-
_BUNDLE_CACHE: dict[tuple[int, str, str, tuple[str, ...]], tuple[Path, str]] = {}
|
|
65
|
+
_BUNDLE_CACHE: dict[tuple[int, str, str, str, tuple[str, ...]], tuple[Path, str]] = {}
|
|
60
66
|
|
|
61
67
|
|
|
62
68
|
def _mount_ref(mount: object) -> dict[str, Any]:
|
|
@@ -133,8 +139,8 @@ def build_manifest(
|
|
|
133
139
|
runtime's compiled camera-sensor bindings in ways undiagnosable from the
|
|
134
140
|
job's own traceback, so this raises — it never skips-and-warns.
|
|
135
141
|
"""
|
|
136
|
-
|
|
137
|
-
module = ".".join(
|
|
142
|
+
entry_file = project_relative_entry_file(app_file, project_root)
|
|
143
|
+
module = ".".join(PurePosixPath(entry_file).with_suffix("").parts)
|
|
138
144
|
|
|
139
145
|
volumes: dict[str, Any] = {}
|
|
140
146
|
resource_by_uri: dict[str, dict[str, Any]] = {}
|
|
@@ -166,7 +172,7 @@ def build_manifest(
|
|
|
166
172
|
# ``local_entrypoint`` remains the stable v2 wire key. The backend
|
|
167
173
|
# runner reads only ``entrypoint.module``, but the complete v2 bytes are
|
|
168
174
|
# pinned by ``test_manifest_wire_stability.py``.
|
|
169
|
-
entrypoint={"file":
|
|
175
|
+
entrypoint={"file": entry_file, "module": module, "local_entrypoint": None},
|
|
170
176
|
runtime_env=dict(getattr(runtime, "runtime_env", {})),
|
|
171
177
|
jobs={name: _job_entry(spec) for name, spec in app.jobs.items()},
|
|
172
178
|
resources=resources,
|
|
@@ -191,9 +197,9 @@ def ensure_bundle(
|
|
|
191
197
|
This is the *submit* primitive: the manifest is built from the live app object
|
|
192
198
|
(no second discovery import) with the submitted job's ``args`` recorded, and
|
|
193
199
|
the source tree is written to ``<out>/<bundle_dir_name(package_id)>/`` — the
|
|
194
|
-
per-submission directory keyed on source + ``job_name`` + ``args`` (named by
|
|
200
|
+
per-submission directory keyed on source + entry file + ``job_name`` + ``args`` (named by
|
|
195
201
|
the id's short, filesystem-safe form; see ``packaging.bundle_dir_name``).
|
|
196
|
-
Memoised per submission (``(id(app), job_name, canonical_json(args), sorted
|
|
202
|
+
Memoised per submission (``(id(app), entry_file, job_name, canonical_json(args), sorted
|
|
197
203
|
extra_assets uris)``) so re-submitting the same job with the same args AND the
|
|
198
204
|
same captured-asset set reuses the package, while a different job, different
|
|
199
205
|
args, or a DIFFERENT captured-asset set (more constructions since an earlier
|
|
@@ -212,8 +218,9 @@ def ensure_bundle(
|
|
|
212
218
|
submission), never served as a silently wrong package.
|
|
213
219
|
"""
|
|
214
220
|
assert_credentials_outside_project(project_root)
|
|
221
|
+
entry_file = project_relative_entry_file(app_file, project_root)
|
|
215
222
|
extra_asset_uris = tuple(sorted({asset.uri for asset in extra_assets}))
|
|
216
|
-
cache_key = (id(app), job_name, canonical_args(args), extra_asset_uris)
|
|
223
|
+
cache_key = (id(app), entry_file, job_name, canonical_args(args), extra_asset_uris)
|
|
217
224
|
cached = _BUNDLE_CACHE.get(cache_key)
|
|
218
225
|
if cached is not None:
|
|
219
226
|
cached_dir, cached_package_id = cached
|