simulo 0.26.0__tar.gz → 0.27.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.27.0}/PKG-INFO +14 -1
- {simulo-0.26.0 → simulo-0.27.0}/PYPI.md +13 -0
- {simulo-0.26.0 → simulo-0.27.0}/pyproject.toml +1 -1
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/cli.py +42 -8
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/http.py +3 -1
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/install_samples.py +138 -4
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/preflight_api.py +4 -12
- {simulo-0.26.0 → simulo-0.27.0/src/simulo.egg-info}/PKG-INFO +14 -1
- {simulo-0.26.0 → simulo-0.27.0}/MANIFEST.in +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/setup.cfg +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/__init__.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/__init__.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/_entrypoint.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/_mounts.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/_runner.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/_secure_downloads.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/app.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/asset.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/asset_api.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/asset_package.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/asset_pins.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/builtin_aliases.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/bundle.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/cancel_api.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/config.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/credentials.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/discovery.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/export_api.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/export_bundle.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/facades.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/identity_api.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/jobs_api.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/learning.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/login.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/mode.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/outputs.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/packaging.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/preflight_render.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/registry.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/runtime.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/runtime_display.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/seed_ref.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/stub.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/submit_api.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/templates/__init__.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/templates/inference/app.py.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/templates/inference/simuloignore.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/templates/scenario/app.py.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/templates/scenario/simuloignore.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/templates/training/app.py.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/templates/training/simuloignore.tmpl +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/view_fragment.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/view_session_api.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/_client/volume.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/callbacks.py +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo/py.typed +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo.egg-info/SOURCES.txt +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo.egg-info/dependency_links.txt +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo.egg-info/entry_points.txt +0 -0
- {simulo-0.26.0 → simulo-0.27.0}/src/simulo.egg-info/requires.txt +0 -0
- {simulo-0.26.0 → simulo-0.27.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.27.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
|
|
@@ -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.27.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"
|
|
@@ -131,6 +131,7 @@ import json
|
|
|
131
131
|
import math
|
|
132
132
|
import os
|
|
133
133
|
import re
|
|
134
|
+
import shlex
|
|
134
135
|
import shutil
|
|
135
136
|
import socket
|
|
136
137
|
import sys
|
|
@@ -194,7 +195,12 @@ from simulo._client.discovery import discover, import_app_module
|
|
|
194
195
|
from simulo._client.export_api import DEFAULT_EXPORT_FORMAT, ExportApiClient, ExportApiError
|
|
195
196
|
from simulo._client.export_bundle import PolicyBundleError, extract_policy_bundle
|
|
196
197
|
from simulo._client.identity_api import IdentityApiClient, IdentityApiError
|
|
197
|
-
from simulo._client.install_samples import
|
|
198
|
+
from simulo._client.install_samples import (
|
|
199
|
+
DEFAULT_SAMPLES_DIRECTORY_NAME,
|
|
200
|
+
InstallSamplesError,
|
|
201
|
+
InstallSamplesUsageError,
|
|
202
|
+
install_samples,
|
|
203
|
+
)
|
|
198
204
|
from simulo._client.jobs_api import JobsApiClient, JobsApiError, JobsApiHTTPError, JobsApiUnavailable
|
|
199
205
|
from simulo._client.login import LoginError, perform_login
|
|
200
206
|
from simulo._client.mode import DISCOVERY
|
|
@@ -8349,14 +8355,33 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
8349
8355
|
install_sub = install.add_subparsers(dest="install_command", required=True)
|
|
8350
8356
|
samples = install_sub.add_parser(
|
|
8351
8357
|
"samples",
|
|
8352
|
-
help="Clone the sample collection into ./simulo-samples with full Git history.",
|
|
8358
|
+
help="Clone the sample collection into ./simulo-samples by default with full Git history.",
|
|
8359
|
+
)
|
|
8360
|
+
samples.add_argument(
|
|
8361
|
+
"--directory",
|
|
8362
|
+
metavar="PATH",
|
|
8363
|
+
type=Path,
|
|
8364
|
+
help="Install into PATH instead of ./simulo-samples. PATH's parent directory must already exist.",
|
|
8365
|
+
)
|
|
8366
|
+
samples.add_argument(
|
|
8367
|
+
"--ref",
|
|
8368
|
+
metavar="REF",
|
|
8369
|
+
help="Check out Git branch, tag, or commit REF after cloning.",
|
|
8353
8370
|
)
|
|
8354
8371
|
|
|
8355
8372
|
def _handle_install_samples(args: argparse.Namespace, extras: Sequence[str]) -> int:
|
|
8356
8373
|
if extras:
|
|
8357
8374
|
samples.error(f"unrecognized arguments: {' '.join(extras)}")
|
|
8375
|
+
directory = args.directory
|
|
8376
|
+
if directory is None:
|
|
8377
|
+
parent_directory = Path.cwd()
|
|
8378
|
+
directory_name = DEFAULT_SAMPLES_DIRECTORY_NAME
|
|
8379
|
+
else:
|
|
8380
|
+
target = directory if directory.is_absolute() else Path.cwd() / directory
|
|
8381
|
+
parent_directory = target.parent
|
|
8382
|
+
directory_name = target.name
|
|
8358
8383
|
try:
|
|
8359
|
-
installed = install_samples(
|
|
8384
|
+
installed = install_samples(parent_directory, directory_name=directory_name, ref=args.ref)
|
|
8360
8385
|
except InstallSamplesUsageError as exc:
|
|
8361
8386
|
print(f"error: {exc}", file=sys.stderr)
|
|
8362
8387
|
print("Nothing was installed.", file=sys.stderr)
|
|
@@ -8365,12 +8390,21 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
8365
8390
|
print(f"error: {exc}", file=sys.stderr)
|
|
8366
8391
|
print("Nothing was installed.", file=sys.stderr)
|
|
8367
8392
|
return 1
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8393
|
+
if args.ref is None:
|
|
8394
|
+
print(
|
|
8395
|
+
f"Installed the Simulo samples in {installed.path} "
|
|
8396
|
+
"(a normal Git clone: history, branches, and git pull all work)."
|
|
8397
|
+
)
|
|
8398
|
+
else:
|
|
8399
|
+
print(
|
|
8400
|
+
f"Installed the Simulo samples in {installed.path} "
|
|
8401
|
+
f"(a normal Git clone pinned to {args.ref!r}; the checkout is detached)."
|
|
8402
|
+
)
|
|
8372
8403
|
print("Next steps:")
|
|
8373
|
-
|
|
8404
|
+
if directory is None:
|
|
8405
|
+
print(" cd simulo-samples")
|
|
8406
|
+
else:
|
|
8407
|
+
print(f" cd {shlex.quote(str(installed.path))}")
|
|
8374
8408
|
print(" cat README.md")
|
|
8375
8409
|
print("README.md is the sample index; each sample folder has its own app.py to run with simulo run.")
|
|
8376
8410
|
return 0
|
|
@@ -48,7 +48,7 @@ class HttpError(RuntimeError):
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
class HttpUnavailable(HttpError):
|
|
51
|
-
"""The endpoint
|
|
51
|
+
"""The endpoint is unreachable or its response times out."""
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
class HttpHTTPError(HttpError):
|
|
@@ -373,6 +373,8 @@ def request_bytes(
|
|
|
373
373
|
raise _structured_http_error(exc) from exc
|
|
374
374
|
except urllib.error.URLError as exc:
|
|
375
375
|
raise HttpUnavailable(_unavailable_message(url, unavailable_hint, exc.reason)) from exc
|
|
376
|
+
except TimeoutError as exc:
|
|
377
|
+
raise HttpUnavailable(_unavailable_message(url, unavailable_hint, exc)) from exc
|
|
376
378
|
|
|
377
379
|
|
|
378
380
|
def request_json(
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
import re
|
|
6
7
|
import shutil
|
|
7
8
|
import subprocess
|
|
8
9
|
import sys
|
|
@@ -134,6 +135,8 @@ def _remove_staging_directory(staging: Path, identity: tuple[int, int]) -> None:
|
|
|
134
135
|
print(f"warning: partial samples install path no longer belongs to this invocation: {staging}", file=sys.stderr)
|
|
135
136
|
return
|
|
136
137
|
try:
|
|
138
|
+
if not os.path.islink(staging):
|
|
139
|
+
os.chmod(staging, 0o700)
|
|
137
140
|
shutil.rmtree(staging, onerror=_remove_read_only_path)
|
|
138
141
|
except OSError:
|
|
139
142
|
print(f"warning: could not remove partial samples install at {staging}", file=sys.stderr)
|
|
@@ -160,6 +163,11 @@ def _target_exists_error(target: Path) -> InstallSamplesUsageError:
|
|
|
160
163
|
return InstallSamplesUsageError(f"{target} already exists.")
|
|
161
164
|
|
|
162
165
|
|
|
166
|
+
def _path_exists(path: Path) -> bool:
|
|
167
|
+
"""Return whether *path* exists, including a dangling symbolic link."""
|
|
168
|
+
return os.path.lexists(path)
|
|
169
|
+
|
|
170
|
+
|
|
163
171
|
def _git_is_reachable_through_working_directory(
|
|
164
172
|
named_git_path: Path, git_executable: Path, working_directory: Path
|
|
165
173
|
) -> bool:
|
|
@@ -190,11 +198,14 @@ def install_samples(
|
|
|
190
198
|
*,
|
|
191
199
|
repository_url: str = SAMPLES_REPOSITORY_URL,
|
|
192
200
|
directory_name: str = DEFAULT_SAMPLES_DIRECTORY_NAME,
|
|
201
|
+
ref: str | None = None,
|
|
193
202
|
timeout_s: int = CLONE_TIMEOUT_S,
|
|
194
203
|
) -> InstalledSamples:
|
|
195
204
|
"""Clone samples into *parent_directory* and reveal them atomically."""
|
|
196
205
|
target = parent_directory / directory_name
|
|
197
|
-
if
|
|
206
|
+
if not parent_directory.is_dir():
|
|
207
|
+
raise InstallSamplesUsageError(f"Parent directory {parent_directory} does not exist or is not a directory.")
|
|
208
|
+
if _path_exists(target):
|
|
198
209
|
raise _target_exists_error(target)
|
|
199
210
|
|
|
200
211
|
git_path = shutil.which("git")
|
|
@@ -223,7 +234,10 @@ def install_samples(
|
|
|
223
234
|
staging_identity = _directory_identity(staging)
|
|
224
235
|
|
|
225
236
|
print("Cloning the samples with full history; this can take a minute.", file=sys.stderr)
|
|
226
|
-
argv = [str(git_executable), "clone"
|
|
237
|
+
argv = [str(git_executable), "clone"]
|
|
238
|
+
if ref is not None:
|
|
239
|
+
argv.append("--no-checkout")
|
|
240
|
+
argv.extend(["--", repository_url, str(staging)])
|
|
227
241
|
try:
|
|
228
242
|
completed = _run_git(argv, timeout_s=timeout_s, env=_git_environment())
|
|
229
243
|
except subprocess.TimeoutExpired as exc:
|
|
@@ -249,11 +263,131 @@ def install_samples(
|
|
|
249
263
|
explain_public_repository=True,
|
|
250
264
|
)
|
|
251
265
|
|
|
252
|
-
if not _clone_has_sample_files(staging):
|
|
266
|
+
if ref is None and not _clone_has_sample_files(staging):
|
|
253
267
|
_remove_staging_directory(staging, staging_identity)
|
|
254
268
|
raise _clone_failure("Git clone produced an empty working tree.", completed.stderr)
|
|
255
269
|
|
|
256
|
-
if
|
|
270
|
+
if ref is not None:
|
|
271
|
+
try:
|
|
272
|
+
resolved = _run_git(
|
|
273
|
+
[
|
|
274
|
+
str(git_executable),
|
|
275
|
+
"-C",
|
|
276
|
+
str(staging),
|
|
277
|
+
"rev-parse",
|
|
278
|
+
"--verify",
|
|
279
|
+
"--end-of-options",
|
|
280
|
+
f"{ref}^{{commit}}",
|
|
281
|
+
],
|
|
282
|
+
timeout_s=timeout_s,
|
|
283
|
+
env=_git_environment(),
|
|
284
|
+
)
|
|
285
|
+
except subprocess.TimeoutExpired as exc:
|
|
286
|
+
_remove_staging_directory(staging, staging_identity)
|
|
287
|
+
raise _clone_failure(f"Git could not resolve samples reference {ref!r}: timed out.", exc.stderr) from None
|
|
288
|
+
except KeyboardInterrupt:
|
|
289
|
+
_remove_staging_directory(staging, staging_identity)
|
|
290
|
+
raise
|
|
291
|
+
except OSError as exc:
|
|
292
|
+
_remove_staging_directory(staging, staging_identity)
|
|
293
|
+
reason = exc.strerror or str(exc)
|
|
294
|
+
raise InstallSamplesError(f"Could not resolve samples reference {ref!r}: {reason}.") from None
|
|
295
|
+
if resolved.returncode != 0:
|
|
296
|
+
try:
|
|
297
|
+
resolved = _run_git(
|
|
298
|
+
[
|
|
299
|
+
str(git_executable),
|
|
300
|
+
"-C",
|
|
301
|
+
str(staging),
|
|
302
|
+
"rev-parse",
|
|
303
|
+
"--verify",
|
|
304
|
+
"--end-of-options",
|
|
305
|
+
f"refs/remotes/origin/{ref}^{{commit}}",
|
|
306
|
+
],
|
|
307
|
+
timeout_s=timeout_s,
|
|
308
|
+
env=_git_environment(),
|
|
309
|
+
)
|
|
310
|
+
except subprocess.TimeoutExpired as exc:
|
|
311
|
+
_remove_staging_directory(staging, staging_identity)
|
|
312
|
+
raise _clone_failure(
|
|
313
|
+
f"Git could not resolve samples reference {ref!r}: timed out.", exc.stderr
|
|
314
|
+
) from None
|
|
315
|
+
except KeyboardInterrupt:
|
|
316
|
+
_remove_staging_directory(staging, staging_identity)
|
|
317
|
+
raise
|
|
318
|
+
except OSError as exc:
|
|
319
|
+
_remove_staging_directory(staging, staging_identity)
|
|
320
|
+
reason = exc.strerror or str(exc)
|
|
321
|
+
raise InstallSamplesError(f"Could not resolve samples reference {ref!r}: {reason}.") from None
|
|
322
|
+
if resolved.returncode != 0:
|
|
323
|
+
_remove_staging_directory(staging, staging_identity)
|
|
324
|
+
raise _clone_failure(f"Git could not resolve samples reference {ref!r}.", resolved.stderr)
|
|
325
|
+
commit = resolved.stdout.strip()
|
|
326
|
+
if re.fullmatch(r"[0-9a-fA-F]{40,64}", commit) is None:
|
|
327
|
+
_remove_staging_directory(staging, staging_identity)
|
|
328
|
+
raise InstallSamplesError(f"Git could not resolve samples reference {ref!r} to a commit.")
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
checked_out = _run_git(
|
|
332
|
+
[str(git_executable), "-C", str(staging), "checkout", "--detach", commit],
|
|
333
|
+
timeout_s=timeout_s,
|
|
334
|
+
env=_git_environment(),
|
|
335
|
+
)
|
|
336
|
+
except subprocess.TimeoutExpired as exc:
|
|
337
|
+
_remove_staging_directory(staging, staging_identity)
|
|
338
|
+
raise _clone_failure(f"Git could not check out samples reference {ref!r}: timed out.", exc.stderr) from None
|
|
339
|
+
except KeyboardInterrupt:
|
|
340
|
+
_remove_staging_directory(staging, staging_identity)
|
|
341
|
+
raise
|
|
342
|
+
except OSError as exc:
|
|
343
|
+
_remove_staging_directory(staging, staging_identity)
|
|
344
|
+
reason = exc.strerror or str(exc)
|
|
345
|
+
raise InstallSamplesError(f"Could not check out samples reference {ref!r}: {reason}.") from None
|
|
346
|
+
if checked_out.returncode != 0:
|
|
347
|
+
_remove_staging_directory(staging, staging_identity)
|
|
348
|
+
raise _clone_failure(f"Git could not check out samples reference {ref!r}.", checked_out.stderr)
|
|
349
|
+
if not _clone_has_sample_files(staging):
|
|
350
|
+
_remove_staging_directory(staging, staging_identity)
|
|
351
|
+
raise _clone_failure("Git checkout produced an empty working tree.", checked_out.stderr)
|
|
352
|
+
|
|
353
|
+
try:
|
|
354
|
+
head = _run_git(
|
|
355
|
+
[str(git_executable), "-C", str(staging), "rev-parse", "--verify", "HEAD"],
|
|
356
|
+
timeout_s=timeout_s,
|
|
357
|
+
env=_git_environment(),
|
|
358
|
+
)
|
|
359
|
+
worktree = _run_git(
|
|
360
|
+
[
|
|
361
|
+
str(git_executable),
|
|
362
|
+
"-C",
|
|
363
|
+
str(staging),
|
|
364
|
+
"diff",
|
|
365
|
+
"--quiet",
|
|
366
|
+
commit,
|
|
367
|
+
"--",
|
|
368
|
+
],
|
|
369
|
+
timeout_s=timeout_s,
|
|
370
|
+
env=_git_environment(),
|
|
371
|
+
)
|
|
372
|
+
except subprocess.TimeoutExpired as exc:
|
|
373
|
+
_remove_staging_directory(staging, staging_identity)
|
|
374
|
+
raise _clone_failure(f"Git could not verify samples reference {ref!r}: timed out.", exc.stderr) from None
|
|
375
|
+
except KeyboardInterrupt:
|
|
376
|
+
_remove_staging_directory(staging, staging_identity)
|
|
377
|
+
raise
|
|
378
|
+
except OSError as exc:
|
|
379
|
+
_remove_staging_directory(staging, staging_identity)
|
|
380
|
+
reason = exc.strerror or str(exc)
|
|
381
|
+
raise InstallSamplesError(f"Could not verify samples reference {ref!r}: {reason}.") from None
|
|
382
|
+
if head.returncode != 0 or worktree.returncode not in (0, 1):
|
|
383
|
+
_remove_staging_directory(staging, staging_identity)
|
|
384
|
+
stderr = head.stderr if head.returncode != 0 else worktree.stderr
|
|
385
|
+
raise _clone_failure(f"Git could not verify samples reference {ref!r}.", stderr)
|
|
386
|
+
if head.stdout.strip().lower() != commit.lower() or worktree.returncode != 0:
|
|
387
|
+
_remove_staging_directory(staging, staging_identity)
|
|
388
|
+
raise InstallSamplesError("Git checkout did not leave the requested samples revision clean.")
|
|
389
|
+
|
|
390
|
+
if _path_exists(target):
|
|
257
391
|
_remove_staging_directory(staging, staging_identity)
|
|
258
392
|
raise _target_exists_error(target)
|
|
259
393
|
|
|
@@ -17,18 +17,10 @@ always returns a :class:`PreflightOutcome`, either a genuine
|
|
|
17
17
|
for the CLI to print verbatim before proceeding. Only a genuinely
|
|
18
18
|
well-formed 200 response can carry a ``blocking`` finding.
|
|
19
19
|
|
|
20
|
-
The
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
but a peer that accepts the connection and then simply never answers (this
|
|
25
|
-
PR's own connection-timeout test reproduces it) times out INSIDE
|
|
26
|
-
``http.client.HTTPConnection.getresponse()`` — a call site ``http.py``'s
|
|
27
|
-
existing ``except urllib.error.URLError`` does not wrap, so a bare
|
|
28
|
-
``socket.timeout``/``TimeoutError`` can reach this call site unwrapped. The
|
|
29
|
-
broad catch is the safety net for exactly that gap (and for anything else
|
|
30
|
-
unanticipated) — preflight must not add a second instance of this failure class to the
|
|
31
|
-
submit path.
|
|
20
|
+
The shared HTTP helper wraps connection failures and response timeouts as
|
|
21
|
+
``HttpUnavailable``. The final ``except Exception`` in
|
|
22
|
+
:meth:`PreflightApiClient.evaluate` preserves the fail-open contract for other
|
|
23
|
+
unexpected errors: only a well-formed findings response may block submission.
|
|
32
24
|
"""
|
|
33
25
|
|
|
34
26
|
from __future__ import annotations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simulo
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.27.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
|
|
@@ -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
|
|
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
|