simulo 0.28.2__tar.gz → 0.29.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.28.2/src/simulo.egg-info → simulo-0.29.0}/PKG-INFO +14 -13
- {simulo-0.28.2 → simulo-0.29.0}/PYPI.md +12 -11
- {simulo-0.28.2 → simulo-0.29.0}/pyproject.toml +3 -3
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/__init__.py +20 -35
- simulo-0.28.2/src/simulo/_client/_entrypoint.py → simulo-0.29.0/src/simulo/_client/_arguments.py +17 -51
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/_runner.py +34 -1
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/_secure_downloads.py +1 -1
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/app.py +360 -281
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/asset.py +121 -6
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/asset_api.py +2 -2
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/asset_package.py +6 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/asset_pins.py +1 -1
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/bundle.py +12 -23
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/cli.py +1011 -1657
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/credentials.py +25 -4
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/discovery.py +114 -12
- simulo-0.29.0/src/simulo/_client/export_api.py +108 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/facades.py +4 -56
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/jobs_api.py +38 -8
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/learning.py +11 -41
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/outputs.py +0 -2
- simulo-0.29.0/src/simulo/_client/policies_api.py +173 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/preflight_api.py +8 -4
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/preflight_render.py +9 -20
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/submit_api.py +73 -318
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/templates/__init__.py +3 -3
- simulo-0.29.0/src/simulo/_client/templates/training/simuloignore.tmpl +14 -0
- simulo-0.29.0/src/simulo/_client/templates/training/task.py.tmpl +108 -0
- simulo-0.29.0/src/simulo/_client/templates/training/train.py.tmpl +23 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/volume.py +10 -5
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/callbacks.py +2 -29
- {simulo-0.28.2 → simulo-0.29.0/src/simulo.egg-info}/PKG-INFO +14 -13
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo.egg-info/SOURCES.txt +5 -8
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo.egg-info/requires.txt +1 -1
- simulo-0.28.2/src/simulo/_client/export_api.py +0 -212
- simulo-0.28.2/src/simulo/_client/seed_ref.py +0 -76
- simulo-0.28.2/src/simulo/_client/templates/inference/app.py.tmpl +0 -316
- simulo-0.28.2/src/simulo/_client/templates/inference/simuloignore.tmpl +0 -30
- simulo-0.28.2/src/simulo/_client/templates/scenario/app.py.tmpl +0 -93
- simulo-0.28.2/src/simulo/_client/templates/scenario/simuloignore.tmpl +0 -27
- simulo-0.28.2/src/simulo/_client/templates/training/app.py.tmpl +0 -235
- simulo-0.28.2/src/simulo/_client/templates/training/simuloignore.tmpl +0 -29
- {simulo-0.28.2 → simulo-0.29.0}/MANIFEST.in +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/setup.cfg +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/__init__.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/_mounts.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/builtin_aliases.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/cancel_api.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/config.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/export_bundle.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/http.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/identity_api.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/install_samples.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/login.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/mode.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/packaging.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/registry.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/runtime.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/runtime_display.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/stub.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/view_fragment.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/_client/view_session_api.py +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo/py.typed +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo.egg-info/dependency_links.txt +0 -0
- {simulo-0.28.2 → simulo-0.29.0}/src/simulo.egg-info/entry_points.txt +0 -0
- {simulo-0.28.2 → simulo-0.29.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.29.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.22,>=0.21.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
|
|
@@ -62,12 +62,11 @@ Simulo is a cloud-native AI platform for robotics simulation and training. Its
|
|
|
62
62
|
lightweight `simulo` Python SDK and CLI let you define robot-learning applications
|
|
63
63
|
locally, submit them to the Simulo cloud, and inspect the results. The platform
|
|
64
64
|
handles managed GPU execution, reproducible packaging, versioned assets,
|
|
65
|
-
|
|
65
|
+
policies, checkpoints, recordings, ONNX bundles, and live 3D viewing.
|
|
66
66
|
|
|
67
67
|
The package installed on your computer is torch-free and requires no local GPU or
|
|
68
|
-
simulator. The managed runtime executes reinforcement-learning training
|
|
69
|
-
|
|
70
|
-
the cloud.
|
|
68
|
+
simulator. The managed runtime executes reinforcement-learning training on
|
|
69
|
+
managed GPUs in the cloud.
|
|
71
70
|
|
|
72
71
|
## What you can do
|
|
73
72
|
|
|
@@ -75,7 +74,7 @@ the cloud.
|
|
|
75
74
|
- Build vectorized reinforcement-learning environments and train robot policies with PPO.
|
|
76
75
|
- Start with validated catalog robots or publish your own OpenUSD (USD) and URDF assets.
|
|
77
76
|
- Continue training from the best or latest checkpoint produced by an earlier job.
|
|
78
|
-
-
|
|
77
|
+
- Export an explicit policy checkpoint as a verified ONNX bundle.
|
|
79
78
|
- Stream logs, retrieve results and outputs, capture MCAP rollout recordings, and watch a live 3D view.
|
|
80
79
|
|
|
81
80
|
## Install
|
|
@@ -93,12 +92,13 @@ Simulo requires Python 3.11 or newer. One installation provides both the
|
|
|
93
92
|
simulo create mybot
|
|
94
93
|
simulo login
|
|
95
94
|
cd mybot
|
|
96
|
-
simulo run
|
|
95
|
+
simulo run train.py --frozen --strict-assets
|
|
97
96
|
```
|
|
98
97
|
|
|
99
98
|
`simulo create` writes a complete starter application to your machine without
|
|
100
|
-
requiring a login or network connection.
|
|
101
|
-
|
|
99
|
+
requiring a login or network connection. It creates `task.py`, `train.py`, and
|
|
100
|
+
`.simuloignore`; the default application trains a PPO policy on a validated cartpole
|
|
101
|
+
asset. `simulo run` packages the source, resolves
|
|
102
102
|
and pins its assets, submits the job, and follows its cloud logs.
|
|
103
103
|
|
|
104
104
|
## Explore robotics samples
|
|
@@ -114,8 +114,9 @@ See the repository README for setup and usage.
|
|
|
114
114
|
simulo jobs # list submitted jobs and status
|
|
115
115
|
simulo logs --follow # stream the latest job's logs
|
|
116
116
|
simulo result # read the job's result
|
|
117
|
-
simulo
|
|
118
|
-
simulo
|
|
117
|
+
simulo policy list # list training policies and checkpoints
|
|
118
|
+
simulo policy get policy_x:best # download a verified checkpoint
|
|
119
|
+
simulo export policy_x:best # export a verified ONNX bundle
|
|
119
120
|
simulo recordings # download MCAP rollout recordings
|
|
120
121
|
simulo outputs # retrieve other saved outputs
|
|
121
122
|
simulo view # view a running --viewstream job in 3D
|
|
@@ -135,7 +136,7 @@ simulo asset publish ./my-robot --kind robot
|
|
|
135
136
|
|
|
136
137
|
Simulo catalogs keep robot, world, and prop assets versioned and traceable. You
|
|
137
138
|
can use validated global assets immediately or publish your own USD or URDF
|
|
138
|
-
package for reuse across training
|
|
139
|
+
package for reuse across training jobs.
|
|
139
140
|
|
|
140
141
|
## Learn more
|
|
141
142
|
|
|
@@ -6,12 +6,11 @@ Simulo is a cloud-native AI platform for robotics simulation and training. Its
|
|
|
6
6
|
lightweight `simulo` Python SDK and CLI let you define robot-learning applications
|
|
7
7
|
locally, submit them to the Simulo cloud, and inspect the results. The platform
|
|
8
8
|
handles managed GPU execution, reproducible packaging, versioned assets,
|
|
9
|
-
|
|
9
|
+
policies, checkpoints, recordings, ONNX bundles, and live 3D viewing.
|
|
10
10
|
|
|
11
11
|
The package installed on your computer is torch-free and requires no local GPU or
|
|
12
|
-
simulator. The managed runtime executes reinforcement-learning training
|
|
13
|
-
|
|
14
|
-
the cloud.
|
|
12
|
+
simulator. The managed runtime executes reinforcement-learning training on
|
|
13
|
+
managed GPUs in the cloud.
|
|
15
14
|
|
|
16
15
|
## What you can do
|
|
17
16
|
|
|
@@ -19,7 +18,7 @@ the cloud.
|
|
|
19
18
|
- Build vectorized reinforcement-learning environments and train robot policies with PPO.
|
|
20
19
|
- Start with validated catalog robots or publish your own OpenUSD (USD) and URDF assets.
|
|
21
20
|
- Continue training from the best or latest checkpoint produced by an earlier job.
|
|
22
|
-
-
|
|
21
|
+
- Export an explicit policy checkpoint as a verified ONNX bundle.
|
|
23
22
|
- Stream logs, retrieve results and outputs, capture MCAP rollout recordings, and watch a live 3D view.
|
|
24
23
|
|
|
25
24
|
## Install
|
|
@@ -37,12 +36,13 @@ Simulo requires Python 3.11 or newer. One installation provides both the
|
|
|
37
36
|
simulo create mybot
|
|
38
37
|
simulo login
|
|
39
38
|
cd mybot
|
|
40
|
-
simulo run
|
|
39
|
+
simulo run train.py --frozen --strict-assets
|
|
41
40
|
```
|
|
42
41
|
|
|
43
42
|
`simulo create` writes a complete starter application to your machine without
|
|
44
|
-
requiring a login or network connection.
|
|
45
|
-
|
|
43
|
+
requiring a login or network connection. It creates `task.py`, `train.py`, and
|
|
44
|
+
`.simuloignore`; the default application trains a PPO policy on a validated cartpole
|
|
45
|
+
asset. `simulo run` packages the source, resolves
|
|
46
46
|
and pins its assets, submits the job, and follows its cloud logs.
|
|
47
47
|
|
|
48
48
|
## Explore robotics samples
|
|
@@ -58,8 +58,9 @@ See the repository README for setup and usage.
|
|
|
58
58
|
simulo jobs # list submitted jobs and status
|
|
59
59
|
simulo logs --follow # stream the latest job's logs
|
|
60
60
|
simulo result # read the job's result
|
|
61
|
-
simulo
|
|
62
|
-
simulo
|
|
61
|
+
simulo policy list # list training policies and checkpoints
|
|
62
|
+
simulo policy get policy_x:best # download a verified checkpoint
|
|
63
|
+
simulo export policy_x:best # export a verified ONNX bundle
|
|
63
64
|
simulo recordings # download MCAP rollout recordings
|
|
64
65
|
simulo outputs # retrieve other saved outputs
|
|
65
66
|
simulo view # view a running --viewstream job in 3D
|
|
@@ -79,7 +80,7 @@ simulo asset publish ./my-robot --kind robot
|
|
|
79
80
|
|
|
80
81
|
Simulo catalogs keep robot, world, and prop assets versioned and traceable. You
|
|
81
82
|
can use validated global assets immediately or publish your own USD or URDF
|
|
82
|
-
package for reuse across training
|
|
83
|
+
package for reuse across training jobs.
|
|
83
84
|
|
|
84
85
|
## Learn more
|
|
85
86
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simulo"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.29.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.21.0,<0.22",
|
|
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
|
|
@@ -157,7 +157,7 @@ namespaces = true
|
|
|
157
157
|
# each is read at runtime via `importlib.resources.files(...)`, never `__file__`
|
|
158
158
|
# path math, so this glob actually landing in the built wheel is load-bearing —
|
|
159
159
|
# see tests/test_create_wheel_packaging.py.
|
|
160
|
-
"simulo._client.templates" = ["training/*.tmpl"
|
|
160
|
+
"simulo._client.templates" = ["training/*.tmpl"]
|
|
161
161
|
|
|
162
162
|
[tool.mypy]
|
|
163
163
|
python_version = "3.11"
|
|
@@ -44,8 +44,8 @@ Three kinds of name live on ``simulo``:
|
|
|
44
44
|
stdlib-only (``NewType`` is the underlying ``str`` at runtime).
|
|
45
45
|
* **Lazy, mode-aware** names — the learning names ``Task / Scene / Robot /
|
|
46
46
|
LearningEnv / RLTrainer / …`` plus the execution-time ``save_output`` and
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
authoring value types described below — are resolved through the PEP 562
|
|
48
|
+
``__getattr__`` below, which delegates to the
|
|
49
49
|
thin client's internal learning-name resolver. At submit (discovery mode) they resolve to
|
|
50
50
|
lean stand-ins so the user app imports without the heavy runtime; only the
|
|
51
51
|
backend runner (execution mode) resolves them to the real implementations.
|
|
@@ -81,16 +81,18 @@ from simulo._client.volume import Volume
|
|
|
81
81
|
# never a second import.
|
|
82
82
|
from simulo.interfaces import (
|
|
83
83
|
CheckpointId,
|
|
84
|
+
CheckpointRefIsJobError,
|
|
84
85
|
ContractViolationError,
|
|
85
86
|
Digest,
|
|
86
87
|
ImportResolutionError,
|
|
87
88
|
JobFailedError,
|
|
88
89
|
JobId,
|
|
89
90
|
JobPublicId,
|
|
90
|
-
|
|
91
|
+
JobType,
|
|
91
92
|
OrganizationId,
|
|
92
93
|
OutputPublicId,
|
|
93
94
|
PackageId,
|
|
95
|
+
PolicyPublicId,
|
|
94
96
|
ProjectId,
|
|
95
97
|
RecordingPublicId,
|
|
96
98
|
ResourceId,
|
|
@@ -99,11 +101,12 @@ from simulo.interfaces import (
|
|
|
99
101
|
ResumeIncompatibleError,
|
|
100
102
|
SimuloError,
|
|
101
103
|
TagId,
|
|
102
|
-
|
|
104
|
+
checkpoint_ref_is_job_hint,
|
|
105
|
+
parse_checkpoint_ref,
|
|
103
106
|
parse_job_public_id,
|
|
104
|
-
parse_model_public_id,
|
|
105
107
|
parse_output_public_id,
|
|
106
108
|
parse_recording_public_id,
|
|
109
|
+
policy_id_for_job,
|
|
107
110
|
)
|
|
108
111
|
|
|
109
112
|
# The authoring value types that the lazy resolver does NOT already carry.
|
|
@@ -179,17 +182,20 @@ __all__ = [
|
|
|
179
182
|
"ProjectId",
|
|
180
183
|
"PackageId",
|
|
181
184
|
"JobId",
|
|
185
|
+
"JobType",
|
|
182
186
|
"JobPublicId",
|
|
183
|
-
"
|
|
187
|
+
"PolicyPublicId",
|
|
184
188
|
"RecordingPublicId",
|
|
185
189
|
"OutputPublicId",
|
|
186
190
|
"parse_job_public_id",
|
|
187
|
-
"
|
|
191
|
+
"parse_checkpoint_ref",
|
|
192
|
+
"policy_id_for_job",
|
|
193
|
+
"checkpoint_ref_is_job_hint",
|
|
194
|
+
"CheckpointRefIsJobError",
|
|
188
195
|
"parse_recording_public_id",
|
|
189
196
|
"parse_output_public_id",
|
|
190
197
|
"ResourceId",
|
|
191
198
|
"CheckpointId",
|
|
192
|
-
"TrainedModelId",
|
|
193
199
|
"TagId",
|
|
194
200
|
"ResourceUri",
|
|
195
201
|
"Digest",
|
|
@@ -256,7 +262,6 @@ __all__ = [
|
|
|
256
262
|
"RewardComponent",
|
|
257
263
|
"RewardSet",
|
|
258
264
|
"Robot",
|
|
259
|
-
"Scenario",
|
|
260
265
|
"Scene",
|
|
261
266
|
"SensorOffset",
|
|
262
267
|
"Simulation",
|
|
@@ -268,7 +273,6 @@ __all__ = [
|
|
|
268
273
|
"Trainer",
|
|
269
274
|
"Visual",
|
|
270
275
|
"World",
|
|
271
|
-
"run",
|
|
272
276
|
"save_output",
|
|
273
277
|
]
|
|
274
278
|
|
|
@@ -283,6 +287,12 @@ def __getattr__(name: str) -> object:
|
|
|
283
287
|
"""
|
|
284
288
|
if name in _LEARNING_NAMES:
|
|
285
289
|
return _resolve_learning(name)
|
|
290
|
+
removed_hints = {
|
|
291
|
+
"Scenario": "simulo.Scenario was removed. Use a typed @app.job instead.",
|
|
292
|
+
"run": "simulo.run was removed. Use `simulo run train.py` instead.",
|
|
293
|
+
}
|
|
294
|
+
if name in removed_hints:
|
|
295
|
+
raise AttributeError(removed_hints[name])
|
|
286
296
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
287
297
|
|
|
288
298
|
|
|
@@ -318,15 +328,6 @@ if TYPE_CHECKING:
|
|
|
318
328
|
# NOT the declared type; see the facades module docstring for the
|
|
319
329
|
# known gap); facade fidelity to the contract AND to the concrete
|
|
320
330
|
# worker class is pinned by the backend suite's conformance engine.
|
|
321
|
-
# * **Called mirror** name (``simulo.run(scenario_class, ...)`` — the one
|
|
322
|
-
# non-learning, non-instantiated member of this kind): same problem as
|
|
323
|
-
# an instantiated mirror one level down — with no override, ``run``
|
|
324
|
-
# types as ``object`` (``__getattr__``'s declared return), and
|
|
325
|
-
# ``object`` is not *callable* at all. It aliases a plain FUNCTION
|
|
326
|
-
# facade (there is no Protocol for a bare function to alias against),
|
|
327
|
-
# never registered in the backend's class-shaped conformance engine —
|
|
328
|
-
# see the facades module docstring for where its drift guard lives
|
|
329
|
-
# instead.
|
|
330
331
|
# * **Annotation-position mirror** names alias their contract Protocol:
|
|
331
332
|
# ``Scene`` is ``SceneProtocol`` — measured across every shipped user
|
|
332
333
|
# surface it appears only as ``def build(self, scene: simulo.Scene)``,
|
|
@@ -380,7 +381,6 @@ if TYPE_CHECKING:
|
|
|
380
381
|
PlayerProtocol,
|
|
381
382
|
PolicyProtocol,
|
|
382
383
|
RewardComponentProtocol,
|
|
383
|
-
ScenarioProtocol,
|
|
384
384
|
TaskProtocol,
|
|
385
385
|
TerminationConditionProtocol,
|
|
386
386
|
TrainerProtocol,
|
|
@@ -389,7 +389,6 @@ if TYPE_CHECKING:
|
|
|
389
389
|
# Subclassable names alias their Protocol so ``class X(simulo.Task)`` is checked
|
|
390
390
|
# against ``TaskProtocol``.
|
|
391
391
|
Task = TaskProtocol
|
|
392
|
-
Scenario = ScenarioProtocol
|
|
393
392
|
Policy = PolicyProtocol
|
|
394
393
|
Player = PlayerProtocol
|
|
395
394
|
Trainer = TrainerProtocol
|
|
@@ -401,20 +400,6 @@ if TYPE_CHECKING:
|
|
|
401
400
|
# ``save_output`` aliases its real execution-mode implementation (a
|
|
402
401
|
# torch-free thin-client function), so ``simulo.save_output("summary",
|
|
403
402
|
# path)`` type-checks with the precise signature instead of ``Any``.
|
|
404
|
-
# ``run`` (the scenario execution loop) aliases its typing FACADE — a
|
|
405
|
-
# plain FUNCTION facade, not a class one (there is no ``RunProtocol`` to
|
|
406
|
-
# alias against; ``run`` is not a contract method). Without this, ``run``
|
|
407
|
-
# falls back to ``__getattr__``'s ``-> object`` return type, and
|
|
408
|
-
# ``object`` is not callable: ``simulo.run(MyScenario, ...)`` in a freshly
|
|
409
|
-
# scaffolded app is a hard mypy error no ``--ignore-missing-imports`` can
|
|
410
|
-
# suppress. The facade's real target (``simulo.scenario.run``) lives in
|
|
411
|
-
# the ``simulo-backend`` distribution — outside this package's own
|
|
412
|
-
# ``MYPYPATH`` — but the facade itself is thin-client-resident code
|
|
413
|
-
# (``simulo/_client/facades.py``), exactly like ``Camera``'s facade for
|
|
414
|
-
# the same reason; see that module's docstring for the full rationale and
|
|
415
|
-
# where drift against the real function is caught (a live, unmocked
|
|
416
|
-
# signature comparison in ``simulo-backend``'s test suite, not mypy).
|
|
417
|
-
from simulo._client.facades import run as run
|
|
418
403
|
from simulo._client.outputs import save_output as save_output
|
|
419
404
|
|
|
420
405
|
# RELOCATED authoring value types (Plan C — Waves C1, C2, then C4's
|
simulo-0.28.2/src/simulo/_client/_entrypoint.py → simulo-0.29.0/src/simulo/_client/_arguments.py
RENAMED
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
"""Map ``simulo run``
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* **The entrypoint path** — ``simulo run app.py`` maps ``--flag value``
|
|
7
|
-
arguments onto the registered ``@app.entrypoint``'s parameters and
|
|
8
|
-
invokes it; the entrypoint's own ``spawn()`` calls end at exactly one
|
|
9
|
-
submit (:func:`run_entrypoint` / :func:`parse_args`). (A direct ``python
|
|
10
|
-
app.py`` never reaches this module: :meth:`App.entrypoint` prints the
|
|
11
|
-
exact equivalent ``simulo run`` command to stderr and exits 2.)
|
|
12
|
-
* **The no-entrypoint path** — with no ``@app.entrypoint`` declared, the
|
|
13
|
-
CLI maps the same ``--flag value`` arguments directly onto the chosen
|
|
14
|
-
``@app.job``'s own signature (:func:`parse_explicit_args`) and spawns it.
|
|
15
|
-
Only EXPLICITLY-passed flags are returned there: a defaulted-but-unpassed
|
|
16
|
-
parameter must stay out of the submitted ``args`` so an upgraded client
|
|
17
|
-
submits the byte-identical package (same ``canonical_args`` → same
|
|
18
|
-
``package_id``) for the no-flags case it always produced ``args={}`` for.
|
|
1
|
+
"""Map ``simulo run`` arguments onto a sole job's signature.
|
|
2
|
+
|
|
3
|
+
Only explicitly passed flags are returned: a defaulted-but-unpassed parameter
|
|
4
|
+
must stay out of submitted ``args`` so the no-flags package identity remains
|
|
5
|
+
stable.
|
|
19
6
|
|
|
20
7
|
An argument parser is built from the target's signature: each parameter
|
|
21
8
|
``foo_bar`` becomes ``--foo-bar`` (dash for underscore), coerced from the
|
|
@@ -135,10 +122,8 @@ def _strip_optional_annotation(ann: Any) -> Any:
|
|
|
135
122
|
def conclusive_param_type(param: inspect.Parameter) -> Optional[type]:
|
|
136
123
|
"""The scalar type a ``--flag`` value is conclusively coerced to, or ``None``.
|
|
137
124
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
the no-entrypoint job path submits the coerced value straight into the
|
|
141
|
-
job's ``args``, so a guess is a silent wrong submission: ``tags: list = []``
|
|
125
|
+
The sole-job path submits the coerced value straight into the job's
|
|
126
|
+
``args``, so a guess is a silent wrong submission: ``tags: list = []``
|
|
142
127
|
would coerce ``--tags release`` through ``list("release")`` into
|
|
143
128
|
``["r", "e", "l", ...]``, and a ``Path``/``set``/enum value would crash
|
|
144
129
|
``json.dumps`` at manifest time. ``None`` here means "reject at submit".
|
|
@@ -146,8 +131,7 @@ def conclusive_param_type(param: inspect.Parameter) -> Optional[type]:
|
|
|
146
131
|
Conclusive: a non-``None`` scalar default (its type), or an ``int`` /
|
|
147
132
|
``float`` / ``bool`` / ``str`` annotation — real type, PEP 563 string, or
|
|
148
133
|
either wrapped in ``Optional[...]`` / ``| None``. An unannotated parameter
|
|
149
|
-
stays ``str`` (the documented text fallback
|
|
150
|
-
convention).
|
|
134
|
+
stays ``str`` (the documented text fallback).
|
|
151
135
|
"""
|
|
152
136
|
default = param.default
|
|
153
137
|
if default is not inspect.Parameter.empty and default is not None:
|
|
@@ -204,11 +188,11 @@ def build_parser(
|
|
|
204
188
|
) -> argparse.ArgumentParser:
|
|
205
189
|
"""Build an ``argparse`` parser from a function's signature.
|
|
206
190
|
|
|
207
|
-
With ``sentinel_defaults=False
|
|
208
|
-
|
|
191
|
+
With ``sentinel_defaults=False``, a defaulted parameter's parser default is
|
|
192
|
+
the signature's own default, so
|
|
209
193
|
``parse_args`` yields EVERY parameter, and values coerce via
|
|
210
194
|
:func:`_param_type` (``str`` fallback included). With
|
|
211
|
-
``sentinel_defaults=True`` (the
|
|
195
|
+
``sentinel_defaults=True`` (the sole-job path), defaulted
|
|
212
196
|
parameters default to the private ``_UNSET`` sentinel instead — so
|
|
213
197
|
:func:`parse_explicit_args` can tell an explicitly-passed flag from an
|
|
214
198
|
untouched default — and values coerce via :func:`conclusive_param_type`
|
|
@@ -217,7 +201,7 @@ def build_parser(
|
|
|
217
201
|
function's own name, as before.
|
|
218
202
|
|
|
219
203
|
``exclude`` drops the named parameters from the parser entirely — the
|
|
220
|
-
|
|
204
|
+
sole-job path passes its DEFERRED unmappable-but-defaulted parameters
|
|
221
205
|
here (see :func:`unmappable_params`), so a flag targeting one — even via
|
|
222
206
|
an argparse abbreviation the caller's exact-token scan cannot see — fails
|
|
223
207
|
at submit as an unrecognized argument rather than being mis-parsed.
|
|
@@ -225,7 +209,7 @@ def build_parser(
|
|
|
225
209
|
sig = inspect.signature(fn)
|
|
226
210
|
doc = (inspect.getdoc(fn) or "").strip().splitlines()
|
|
227
211
|
parser = argparse.ArgumentParser(
|
|
228
|
-
prog=prog if prog is not None else getattr(fn, "__name__", "
|
|
212
|
+
prog=prog if prog is not None else getattr(fn, "__name__", "job"),
|
|
229
213
|
description=doc[0] if doc else None,
|
|
230
214
|
)
|
|
231
215
|
for name, param in sig.parameters.items():
|
|
@@ -264,19 +248,6 @@ def build_parser(
|
|
|
264
248
|
return parser
|
|
265
249
|
|
|
266
250
|
|
|
267
|
-
def parse_args(fn: Callable[..., object], argv: Sequence[str]) -> dict[str, Any]:
|
|
268
|
-
"""Parse ``argv`` against ``fn``'s signature into a keyword-argument dict."""
|
|
269
|
-
parser = build_parser(fn)
|
|
270
|
-
namespace = parser.parse_args(list(argv))
|
|
271
|
-
sig = inspect.signature(fn)
|
|
272
|
-
kwargs: dict[str, Any] = {}
|
|
273
|
-
for name, param in sig.parameters.items():
|
|
274
|
-
if param.kind in _SKIP_KINDS:
|
|
275
|
-
continue
|
|
276
|
-
kwargs[name] = getattr(namespace, name)
|
|
277
|
-
return kwargs
|
|
278
|
-
|
|
279
|
-
|
|
280
251
|
def parse_explicit_args(
|
|
281
252
|
fn: Callable[..., object],
|
|
282
253
|
argv: Sequence[str],
|
|
@@ -286,13 +257,13 @@ def parse_explicit_args(
|
|
|
286
257
|
) -> dict[str, Any]:
|
|
287
258
|
"""Parse ``argv`` against ``fn``'s signature; return ONLY explicitly-passed kwargs.
|
|
288
259
|
|
|
289
|
-
The
|
|
290
|
-
|
|
260
|
+
The sole-job submit path maps CLI flags onto a ``@app.job`` signature: a
|
|
261
|
+
parameter the user did not pass stays OUT of the
|
|
291
262
|
returned dict (the job body applies its own default on the worker), so the
|
|
292
263
|
no-flags case still submits ``args={}`` — exactly what pre-mapping clients
|
|
293
264
|
always submitted — and ``package_id`` is unchanged across the upgrade.
|
|
294
|
-
A parameter without a default is required
|
|
295
|
-
|
|
265
|
+
A parameter without a default is required. ``exclude`` names parameters
|
|
266
|
+
left out of the parser and the result
|
|
296
267
|
(the deferred unmappable-but-defaulted ones — see :func:`build_parser`).
|
|
297
268
|
"""
|
|
298
269
|
parser = build_parser(fn, sentinel_defaults=True, prog=prog, exclude=exclude)
|
|
@@ -306,8 +277,3 @@ def parse_explicit_args(
|
|
|
306
277
|
if value is not _UNSET:
|
|
307
278
|
kwargs[name] = value
|
|
308
279
|
return kwargs
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
def run_entrypoint(fn: Callable[..., object], argv: Sequence[str]) -> object:
|
|
312
|
-
"""Parse ``argv`` for ``fn`` and invoke it (the single submit happens inside)."""
|
|
313
|
-
return fn(**parse_args(fn, argv))
|
|
@@ -26,7 +26,7 @@ from pathlib import Path
|
|
|
26
26
|
from typing import Any, NoReturn, Optional
|
|
27
27
|
|
|
28
28
|
from simulo.interfaces.ids import JobId, JobPublicId
|
|
29
|
-
from simulo.interfaces.platform.enums import JobStatus
|
|
29
|
+
from simulo.interfaces.platform.enums import JobStatus, JobType
|
|
30
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.
|
|
@@ -90,6 +90,10 @@ class JobHandle:
|
|
|
90
90
|
cloud: bool = False,
|
|
91
91
|
base_url: Optional[str] = None,
|
|
92
92
|
token: Optional[str] = None,
|
|
93
|
+
job_type: Optional[JobType] = None,
|
|
94
|
+
input_checkpoint: Optional[str] = None,
|
|
95
|
+
deferred_stdout: str = "",
|
|
96
|
+
deferred_stderr: str = "",
|
|
93
97
|
) -> None:
|
|
94
98
|
self._job_id = job_id
|
|
95
99
|
self._public_id = public_id
|
|
@@ -98,6 +102,10 @@ class JobHandle:
|
|
|
98
102
|
self._cloud = cloud
|
|
99
103
|
self._base_url = base_url
|
|
100
104
|
self._token = token
|
|
105
|
+
self._job_type = job_type
|
|
106
|
+
self._input_checkpoint = input_checkpoint
|
|
107
|
+
self._deferred_stdout = deferred_stdout
|
|
108
|
+
self._deferred_stderr = deferred_stderr
|
|
101
109
|
|
|
102
110
|
@property
|
|
103
111
|
def job_id(self) -> JobId:
|
|
@@ -134,6 +142,31 @@ class JobHandle:
|
|
|
134
142
|
"""The bearer token to observe this job with (cloud mode only)."""
|
|
135
143
|
return self._token
|
|
136
144
|
|
|
145
|
+
@property
|
|
146
|
+
def job_type(self) -> Optional[JobType]:
|
|
147
|
+
"""Declared type for a cloud submission; absent on legacy local handles."""
|
|
148
|
+
return self._job_type
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def input_checkpoint(self) -> Optional[str]:
|
|
152
|
+
"""Policy checkpoint requested with ``simulo run --from``."""
|
|
153
|
+
return self._input_checkpoint
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def deferred_stdout(self) -> str:
|
|
157
|
+
"""Submit-time output held until the cloud job header is printed."""
|
|
158
|
+
return self._deferred_stdout
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def deferred_stderr(self) -> str:
|
|
162
|
+
"""Submit-time stderr held until the cloud job header is printed."""
|
|
163
|
+
return self._deferred_stderr
|
|
164
|
+
|
|
165
|
+
def _defer_submit_output(self, *, stdout: str = "", stderr: str = "") -> None:
|
|
166
|
+
"""Append client-owned submit output for rendering after the job header."""
|
|
167
|
+
self._deferred_stdout += stdout
|
|
168
|
+
self._deferred_stderr += stderr
|
|
169
|
+
|
|
137
170
|
def execute_hint(self) -> str:
|
|
138
171
|
"""The command that executes this submitted package locally (local-disk mode)."""
|
|
139
172
|
return f"simulo-backend run-package {self._package_path} --job {self._job_name}"
|
|
@@ -4,7 +4,7 @@ THE RACE THIS CLOSES. The bulk download path validates its assembled
|
|
|
4
4
|
filename and the selected destination directory as PATH STRINGS
|
|
5
5
|
(``_contained_bulk_download_destination`` in ``cli.py``: resolve, then
|
|
6
6
|
``relative_to``) before any bytes are fetched. The actual write — inside
|
|
7
|
-
this client's
|
|
7
|
+
this client's checkpoint/output/recording download paths
|
|
8
8
|
— re-derives the SAME destination from its path string again, after a
|
|
9
9
|
network fetch: an ``exist_ok=True`` ``mkdir`` (which silently ACCEPTS a
|
|
10
10
|
symlink planted at a previously-missing path, so long as it resolves to a
|