simulo-interfaces 0.5.0__tar.gz → 0.6.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_interfaces-0.5.0/src/simulo_interfaces.egg-info → simulo_interfaces-0.6.0}/PKG-INFO +1 -1
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/pyproject.toml +1 -1
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/__init__.py +6 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/asset_catalog.py +1 -1
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/enums.py +6 -7
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/runtime.py +12 -5
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/submit.py +268 -1
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0/src/simulo_interfaces.egg-info}/PKG-INFO +1 -1
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/MANIFEST.in +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/PYPI.md +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/setup.cfg +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/__init__.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/exceptions.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/ids.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/app.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/asset.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/callbacks.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/debug.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/domain.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/runs.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/volume.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/py.typed +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/__init__.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/anomaly.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/components.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/env.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/player.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/policy.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/scenario.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/task.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/tensors.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/trainer.py +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo_interfaces.egg-info/SOURCES.txt +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo_interfaces.egg-info/dependency_links.txt +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo_interfaces.egg-info/requires.txt +0 -0
- {simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo_interfaces.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simulo-interfaces"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.0"
|
|
8
8
|
description = "Type contracts for the Simulo SDK. Installed automatically as a dependency of the simulo package."
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.11"
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/__init__.py
RENAMED
|
@@ -109,6 +109,7 @@ from simulo.interfaces.platform.submit import (
|
|
|
109
109
|
MODEL_KINDS,
|
|
110
110
|
PACKAGE_ARCHIVE_ROUTE_TEMPLATE,
|
|
111
111
|
PACKAGES_ROUTE,
|
|
112
|
+
PLATFORM_PINNED_PACKAGES,
|
|
112
113
|
RESERVED_RUNTIME_ENV_KEYS,
|
|
113
114
|
RESERVED_RUNTIME_ENV_PREFIXES,
|
|
114
115
|
SEED_JOB_REF_GRAMMAR,
|
|
@@ -125,6 +126,8 @@ from simulo.interfaces.platform.submit import (
|
|
|
125
126
|
WORKER_LEASE_HEADER,
|
|
126
127
|
ModelRecord,
|
|
127
128
|
RecordingRecord,
|
|
129
|
+
canonical_pypi_name,
|
|
130
|
+
is_platform_pinned,
|
|
128
131
|
)
|
|
129
132
|
from simulo.interfaces.platform.volume import VolumeProtocol
|
|
130
133
|
|
|
@@ -181,6 +184,9 @@ __all__ = [
|
|
|
181
184
|
"RecordingRecord",
|
|
182
185
|
"RESERVED_RUNTIME_ENV_KEYS",
|
|
183
186
|
"RESERVED_RUNTIME_ENV_PREFIXES",
|
|
187
|
+
"PLATFORM_PINNED_PACKAGES",
|
|
188
|
+
"is_platform_pinned",
|
|
189
|
+
"canonical_pypi_name",
|
|
184
190
|
# explicit-run-intent plan: submit-time seed source (additive)
|
|
185
191
|
"SEED_SOURCE_FIELDS",
|
|
186
192
|
"SEED_SOURCE_CONFLICT_CODE",
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/asset_catalog.py
RENAMED
|
@@ -256,7 +256,7 @@ GLOBAL_ASSET_PUBLISHER = "simulo"
|
|
|
256
256
|
#: optional group being absent, 3+ chars matches via the group, but exactly 2
|
|
257
257
|
#: chars can match neither — incoherent for users. Fixed here to ``{0,61}``,
|
|
258
258
|
#: which accepts every length 1–63 contiguously; see the PR-1 resolutions in
|
|
259
|
-
#: ``context/implementation-plans/usd-asset-catalogs/api-contract-freeze.md``.)
|
|
259
|
+
#: ``context/implementation-plans/archive/usd-asset-catalogs/api-contract-freeze.md``.)
|
|
260
260
|
#: Pinned here (rather than imported) because ``simulo-interfaces`` has zero
|
|
261
261
|
#: dependencies on the control plane — the value is copied deliberately and this
|
|
262
262
|
#: docstring is the drift anchor.
|
|
@@ -72,13 +72,12 @@ class ResourceKind(StrEnum):
|
|
|
72
72
|
"""A configuration bundle."""
|
|
73
73
|
|
|
74
74
|
WORLD = "world"
|
|
75
|
-
"""A world
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"
|
|
81
|
-
retained (append-only); neither replaces the other."""
|
|
75
|
+
"""A catalog world — a facility, terrain, or scene robots operate in.
|
|
76
|
+
|
|
77
|
+
``WORLD`` is the current catalog term. The older ``ENVIRONMENT`` resource
|
|
78
|
+
mount term denotes the same concept; both values remain wire-stable and
|
|
79
|
+
neither replaces the other.
|
|
80
|
+
"""
|
|
82
81
|
|
|
83
82
|
|
|
84
83
|
class ArtifactKind(StrEnum):
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/runtime.py
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"""``RuntimeProtocol`` — Simulo-curated runtime contract (Part B, Rule #4).
|
|
2
2
|
|
|
3
3
|
Runtimes are Simulo-owned, prebuilt execution environments. The contract is
|
|
4
|
-
deliberately
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
needs.
|
|
4
|
+
deliberately narrow: pick a curated runtime by name, merge in a guarded set
|
|
5
|
+
of user environment variables via ``env()``, and defer heavy imports behind
|
|
6
|
+
``imports()``. There is no dependency-declaration or image-customisation
|
|
7
|
+
surface — the platform runtime ships everything a job needs.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from contextlib import AbstractContextManager
|
|
11
|
-
from typing import Protocol, runtime_checkable
|
|
11
|
+
from typing import Mapping, Protocol, runtime_checkable
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@runtime_checkable
|
|
@@ -17,6 +17,12 @@ class RuntimeProtocol(Protocol):
|
|
|
17
17
|
|
|
18
18
|
Simulo owns and builds every runtime; ``from_registry`` picks one of the
|
|
19
19
|
curated runtimes by name (there is no bring-your-own-image path).
|
|
20
|
+
``env()`` merges caller-supplied environment variables into the job's
|
|
21
|
+
execution environment (later calls win on key conflicts) — guarded: a
|
|
22
|
+
runner/worker-owned key (see
|
|
23
|
+
``simulo.interfaces.platform.submit.RESERVED_RUNTIME_ENV_KEYS`` /
|
|
24
|
+
``RESERVED_RUNTIME_ENV_PREFIXES``) is never honoured; enforcement lives at
|
|
25
|
+
the runner/worker, not in this implementation-free Protocol.
|
|
20
26
|
``imports()`` is the real remote-only-import boundary (Rule #3): locally it
|
|
21
27
|
records deferred import intent; in the cloud it resolves the real imports.
|
|
22
28
|
Heavy imports (torch and friends) live ONLY inside this context manager.
|
|
@@ -24,4 +30,5 @@ class RuntimeProtocol(Protocol):
|
|
|
24
30
|
|
|
25
31
|
@classmethod
|
|
26
32
|
def from_registry(cls, image: str) -> "RuntimeProtocol": ...
|
|
33
|
+
def env(self, variables: Mapping[str, str]) -> "RuntimeProtocol": ...
|
|
27
34
|
def imports(self) -> AbstractContextManager[None]: ...
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/submit.py
RENAMED
|
@@ -124,7 +124,7 @@ platform-standard shape: ``{"error": {"code": ..., "message": ..., "request_id":
|
|
|
124
124
|
terminal status, artifact kind) is server-side business logic layered on
|
|
125
125
|
top of this wire shape, not defined by this module — see the
|
|
126
126
|
explicit-run-intent plan's "Server validation" section
|
|
127
|
-
(``context/implementation-plans/explicit-run-intent-plan.md``).
|
|
127
|
+
(``context/implementation-plans/archive/explicit-run-intent-plan.md``).
|
|
128
128
|
|
|
129
129
|
## Client-facing: recordings
|
|
130
130
|
|
|
@@ -325,6 +325,7 @@ models; the list is empty, never an error.
|
|
|
325
325
|
recordings upload.
|
|
326
326
|
"""
|
|
327
327
|
|
|
328
|
+
import re
|
|
328
329
|
from dataclasses import dataclass
|
|
329
330
|
from typing import Optional
|
|
330
331
|
|
|
@@ -723,3 +724,269 @@ RESERVED_RUNTIME_ENV_KEYS = frozenset(
|
|
|
723
724
|
#: ``SIMULO_CHECKPOINT_KEEP_LAST`` / ``SIMULO_RESUME``) does not match any of
|
|
724
725
|
#: these prefixes and remains legitimate user-supplied job env.
|
|
725
726
|
RESERVED_RUNTIME_ENV_PREFIXES = ("SIMULO_WORKER_", "SIMULO_INTERNAL_", "SIMULO_VOLUME_", "SIMULO_ASSET_")
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
# --------------------------------------------------------------------------
|
|
730
|
+
# Platform-pinned packages
|
|
731
|
+
# --------------------------------------------------------------------------
|
|
732
|
+
#
|
|
733
|
+
# The governing principle: anything already in the base runtime image is
|
|
734
|
+
# denied — a user ``pip_install()`` (restored in a later PR of this wave) may
|
|
735
|
+
# ADD packages the base lacks, but may never OVERRIDE one the base already
|
|
736
|
+
# provides. If a job's layer resolves a different version of a package the
|
|
737
|
+
# base already ships, whichever version resolution order wins can silently
|
|
738
|
+
# break the runtime in ways that never surface in the job's own code. The
|
|
739
|
+
# canonical case (#285): a job's ``pip_install("numpy")`` pulled numpy 2.x,
|
|
740
|
+
# shadowing the platform's numpy-1.x install and breaking the runtime's
|
|
741
|
+
# compiled camera-sensor bindings — the job never imported numpy directly,
|
|
742
|
+
# so the failure was undiagnosable from the job's own traceback. This is the
|
|
743
|
+
# same class of hazard :data:`RESERVED_RUNTIME_ENV_KEYS` guards against for
|
|
744
|
+
# environment variables, applied to pip package names instead: a job must
|
|
745
|
+
# never be able to shadow a platform-pinned dependency.
|
|
746
|
+
#
|
|
747
|
+
# :data:`PLATFORM_PINNED_PACKAGES` is the CLIENT-SIDE FAST-FAIL SUBSET, not
|
|
748
|
+
# the full "anything already in base" set — it names the known-critical base
|
|
749
|
+
# packages we can enumerate statically (the torch family, numpy, the
|
|
750
|
+
# nvidia/CUDA wheels, the platform's own SDK distributions) so
|
|
751
|
+
# ``pip_install()`` (later PR) can reject an obviously-doomed layer at
|
|
752
|
+
# submit time, before a job ever reaches the cloud. It is deliberately
|
|
753
|
+
# curated-but-generous, not exhaustive: the AUTHORITATIVE enforcement lives
|
|
754
|
+
# in ``simulo-backend`` (later PR), which checks each user-requested package
|
|
755
|
+
# against the worker's actual base-env installed set at install time —
|
|
756
|
+
# that check is complete by construction (it reads the real base env, it
|
|
757
|
+
# doesn't hand-maintain a mirror of it) and is what closes the gap this
|
|
758
|
+
# static frozenset cannot.
|
|
759
|
+
#
|
|
760
|
+
# Engine/runtime-native libraries (e.g. an ``isaacsim`` / ``isaaclab`` /
|
|
761
|
+
# ``omni.*``-shaped dependency) are image-baked into the base runtime, not
|
|
762
|
+
# resolved via ``pip`` into site-packages at job time — there is no pip
|
|
763
|
+
# package name a user's ``pip_install()`` could collide with for those, so
|
|
764
|
+
# they are deliberately NOT listed here. The nvidia/CUDA wheels are the
|
|
765
|
+
# opposite case: torch's CUDA build pulls them in via a normal ``pip``
|
|
766
|
+
# dependency into site-packages (the ``nvidia-*-cu12`` family + ``triton``),
|
|
767
|
+
# so a job absolutely could shadow one — they belong in this set. Only
|
|
768
|
+
# packages a job could actually reach through a real ``pip install <name>``
|
|
769
|
+
# belong here, including the platform's own SDK distributions (a job pinning
|
|
770
|
+
# its own ``simulo``/``simulo-interfaces``/``simulo-backend`` version could
|
|
771
|
+
# silently desync the client and runtime contract versions).
|
|
772
|
+
PLATFORM_PINNED_PACKAGES = frozenset(
|
|
773
|
+
{
|
|
774
|
+
"torch",
|
|
775
|
+
"torchvision",
|
|
776
|
+
"torchaudio",
|
|
777
|
+
"numpy",
|
|
778
|
+
"simulo",
|
|
779
|
+
"simulo-interfaces",
|
|
780
|
+
"simulo-backend",
|
|
781
|
+
"triton",
|
|
782
|
+
"nvidia-cuda-runtime-cu12",
|
|
783
|
+
"nvidia-cuda-nvrtc-cu12",
|
|
784
|
+
"nvidia-cuda-cupti-cu12",
|
|
785
|
+
"nvidia-cudnn-cu12",
|
|
786
|
+
"nvidia-cublas-cu12",
|
|
787
|
+
"nvidia-cufft-cu12",
|
|
788
|
+
"nvidia-curand-cu12",
|
|
789
|
+
"nvidia-cusolver-cu12",
|
|
790
|
+
"nvidia-cusparse-cu12",
|
|
791
|
+
"nvidia-nccl-cu12",
|
|
792
|
+
"nvidia-nvtx-cu12",
|
|
793
|
+
"nvidia-nvjitlink-cu12",
|
|
794
|
+
}
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
#: Matches the CUDA-wheel naming shape torch's CUDA build actually pulls into
|
|
798
|
+
#: site-packages — ``nvidia-<lib>-cu<major>`` (``nvidia-cublas-cu12``,
|
|
799
|
+
#: ``nvidia-cusolver-cu13``, ...) — matched in addition to the exact names
|
|
800
|
+
#: enumerated in :data:`PLATFORM_PINNED_PACKAGES` above. CUDA major-version
|
|
801
|
+
#: bumps mint new package names (``-cu12`` becomes ``-cu13`` and so on), so a
|
|
802
|
+
#: shape match keeps every CUDA runtime wheel denied even as the exact-name
|
|
803
|
+
#: list drifts behind a new CUDA release. Deliberately NOT a broad ``nvidia-``
|
|
804
|
+
#: prefix: that wrongly caught legitimate nvidia *helper* packages that do not
|
|
805
|
+
#: shadow anything in the base runtime (``nvidia-ml-py``,
|
|
806
|
+
#: ``nvidia-dali-cuda120`` — note ``-cuda120`` is not the ``-cu<major>``
|
|
807
|
+
#: runtime-wheel suffix), which a job may genuinely want to add.
|
|
808
|
+
_NVIDIA_CUDA_WHEEL_PATTERN = re.compile(r"^nvidia-.+-cu\d+$")
|
|
809
|
+
|
|
810
|
+
# --------------------------------------------------------------------------
|
|
811
|
+
# Plain-PyPI requirement validation (shared: is_platform_pinned below AND the
|
|
812
|
+
# thin client's Runtime.pip_install() both call canonical_pypi_name — see its
|
|
813
|
+
# docstring for why this is a POSITIVE allowlist, not a denylist)
|
|
814
|
+
# --------------------------------------------------------------------------
|
|
815
|
+
#
|
|
816
|
+
# Review round 2 (security MEDIUM + critic MAJOR) replaced an earlier
|
|
817
|
+
# character/prefix DENYLIST here. A denylist only catches the forms its
|
|
818
|
+
# author enumerated; this one leaked on every form it didn't:
|
|
819
|
+
#
|
|
820
|
+
# * ``"numpy(>=1.0)"`` — a parenthesized version spec (legal PEP 508) split
|
|
821
|
+
# its bare name as ``"numpy("`` under the old boundary-character split,
|
|
822
|
+
# missing :data:`PLATFORM_PINNED_PACKAGES` entirely (security MEDIUM).
|
|
823
|
+
# * ``"evil-1.0-py3-none-any.whl"`` / ``"pkg.tar.gz"`` — a bare archive
|
|
824
|
+
# filename contains none of the old denylist's banned characters (no
|
|
825
|
+
# ``@``/``/``/``:``/``\\``, no ``git+``/``./``/``../`` prefix) and sailed
|
|
826
|
+
# through as a "clean" package name; the worker would then do a LOCAL
|
|
827
|
+
# wheel/sdist install — pip runs ``setup.py``/build hooks for those,
|
|
828
|
+
# arbitrary code execution (critic MAJOR).
|
|
829
|
+
# * ``"."``, ``"-e"``, ``"--no-index"``, ``"--pre"`` — a bare directory or a
|
|
830
|
+
# leading-dash pip OPTION likewise contains none of the banned characters;
|
|
831
|
+
# pip would either install the current directory (running ``setup.py``) or
|
|
832
|
+
# have an option string injected into its own argv (critic MAJOR).
|
|
833
|
+
#
|
|
834
|
+
# :func:`canonical_pypi_name` fixes the class of bug, not just these three
|
|
835
|
+
# instances: it is a POSITIVE grammar match for "plain, index-resolved PEP
|
|
836
|
+
# 508 requirement, no direct reference" — anything that does not match,
|
|
837
|
+
# including forms nobody has thought of yet, is rejected by construction.
|
|
838
|
+
# This is also the ONE place both consumers call: ``is_platform_pinned``
|
|
839
|
+
# below and ``simulo._client.runtime.Runtime.pip_install`` (thin client) —
|
|
840
|
+
# duplicating the rule in both packages is how one gets hardened here and
|
|
841
|
+
# missed there (critic MINOR, review round 2).
|
|
842
|
+
|
|
843
|
+
#: PEP 503-shaped bare identifier: one or more alphanumeric runs separated by
|
|
844
|
+
#: single ``.``/``_``/``-`` runs; first and last character always
|
|
845
|
+
#: alphanumeric (so a bare ``.``/``..`` never matches). Written as
|
|
846
|
+
#: alternating alnum-run/separator-run groups — not the more obvious
|
|
847
|
+
#: ``[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?`` — specifically to avoid that
|
|
848
|
+
#: form's ambiguous internal split (two adjacent classes sharing alnum
|
|
849
|
+
#: characters), which is a classic catastrophic-backtracking shape; this
|
|
850
|
+
#: form has none (each alternative consumes a disjoint character class).
|
|
851
|
+
_PEP503_NAME = r"[A-Za-z0-9]+(?:[._-]+[A-Za-z0-9]+)*"
|
|
852
|
+
_EXTRAS = rf"\[\s*{_PEP503_NAME}(?:\s*,\s*{_PEP503_NAME})*\s*\]"
|
|
853
|
+
_VERSION_CMP = r"(?:===|==|!=|<=|>=|~=|<|>)"
|
|
854
|
+
_VERSION_TOKEN = r"[A-Za-z0-9_.*+!-]+"
|
|
855
|
+
_VERSION_CLAUSE = rf"{_VERSION_CMP}\s*{_VERSION_TOKEN}"
|
|
856
|
+
_VERSION_CLAUSES = rf"{_VERSION_CLAUSE}(?:\s*,\s*{_VERSION_CLAUSE})*"
|
|
857
|
+
#: A version spec may optionally be wrapped in parens (``"numpy(>=1.0)"``) —
|
|
858
|
+
#: PEP 508 permits this form; it is the exact shape the old denylist's
|
|
859
|
+
#: name-boundary split missed (see the module note above).
|
|
860
|
+
_VERSION_SPEC = rf"(?:\(\s*{_VERSION_CLAUSES}\s*\)|{_VERSION_CLAUSES})"
|
|
861
|
+
#: Environment-marker body after ``;``. Not a full PEP 508 marker-grammar
|
|
862
|
+
#: parse (boolean ``and``/``or``, string-literal quoting rules) — a safe
|
|
863
|
+
#: CHARACTER allowlist is sufficient here: ``@`` ``/`` ``\\`` are not in this
|
|
864
|
+
#: class, so a marker can never smuggle a direct reference or a path — any
|
|
865
|
+
#: attempt fails the OVERALL match, same as everywhere else in this grammar.
|
|
866
|
+
_MARKER_BODY = r"[A-Za-z0-9_.,()'\" <>=!~-]+"
|
|
867
|
+
_MARKER = rf";\s*{_MARKER_BODY}"
|
|
868
|
+
|
|
869
|
+
#: The full grammar: ``name[extras](version-spec)?(; marker)?``, anchored
|
|
870
|
+
#: start-to-end (via :func:`canonical_pypi_name`'s ``.match`` against the
|
|
871
|
+
#: WHOLE stripped string) — nothing may follow that this grammar doesn't
|
|
872
|
+
#: account for.
|
|
873
|
+
_PLAIN_PYPI_SPEC = re.compile(
|
|
874
|
+
rf"^\s*(?P<name>{_PEP503_NAME})"
|
|
875
|
+
rf"(?:\s*(?P<extras>{_EXTRAS}))?"
|
|
876
|
+
rf"(?:\s*(?P<version>{_VERSION_SPEC}))?"
|
|
877
|
+
rf"(?:\s*{_MARKER})?"
|
|
878
|
+
r"\s*$"
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
#: Archive-file suffixes pip installs directly from a local/downloaded file
|
|
882
|
+
#: without touching the index. Checked explicitly (case-insensitive) because
|
|
883
|
+
#: a bare archive filename is otherwise a SYNTACTICALLY valid PEP 503 name —
|
|
884
|
+
#: ``"evil-1.0-py3-none-any.whl"`` matches :data:`_PLAIN_PYPI_SPEC` letter
|
|
885
|
+
#: for letter (hyphens and dots are legal name characters) — so the grammar
|
|
886
|
+
#: match alone cannot reject it; this suffix check is the other half of
|
|
887
|
+
#: closing that gap (a bare ``packaging.Requirement`` parse has the identical
|
|
888
|
+
#: gap: ``Requirement("evil.whl")`` also parses as a plain name).
|
|
889
|
+
#:
|
|
890
|
+
#: Review round 3 (critic MAJOR / security HIGH): the round-2 tuple named
|
|
891
|
+
#: only 6 suffixes — a subset of pip's real recognized-archive set
|
|
892
|
+
#: (``pip._internal.utils.filetypes.ARCHIVE_EXTENSIONS`` /
|
|
893
|
+
#: ``TAR_EXTENSIONS`` + ``ZIP_EXTENSIONS`` + ``.whl``). ``"evil-1.0.tar"``,
|
|
894
|
+
#: ``"pkg.tbz"``, ``"pkg.tar.xz"``, ``"pkg.txz"``, ``"pkg.tlz"``,
|
|
895
|
+
#: ``"pkg.tar.lz"``, and ``"pkg.tar.lzma"`` (``.tar`` especially — a
|
|
896
|
+
#: mainstream sdist format) all had none of the 6 listed suffixes, so they
|
|
897
|
+
#: read as a clean canonical name and passed BOTH :func:`is_platform_pinned`
|
|
898
|
+
#: and the thin client's ``pip_install`` reject — pip installs each of these
|
|
899
|
+
#: as a local sdist and runs its ``setup.py``. This tuple is now pip's
|
|
900
|
+
#: COMPLETE 12-extension archive set, hardcoded (never `import pip` here —
|
|
901
|
+
#: this package stays torch-free/dependency-light) plus ``.egg`` kept as a
|
|
902
|
+
#: harmless extra (not one of pip's 12, but also not a legitimate bare PyPI
|
|
903
|
+
#: name suffix).
|
|
904
|
+
_ARCHIVE_SUFFIXES = (
|
|
905
|
+
".zip",
|
|
906
|
+
".whl",
|
|
907
|
+
".tar.bz2",
|
|
908
|
+
".tbz",
|
|
909
|
+
".tar.gz",
|
|
910
|
+
".tgz",
|
|
911
|
+
".tar",
|
|
912
|
+
".tar.xz",
|
|
913
|
+
".txz",
|
|
914
|
+
".tlz",
|
|
915
|
+
".tar.lz",
|
|
916
|
+
".tar.lzma",
|
|
917
|
+
".egg",
|
|
918
|
+
)
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
def canonical_pypi_name(spec: str) -> Optional[str]:
|
|
922
|
+
"""Return the canonical (PEP 503-normalised) bare name if *spec* is a
|
|
923
|
+
well-formed, plain-PyPI-installable requirement; ``None`` otherwise.
|
|
924
|
+
|
|
925
|
+
A spec is valid ONLY if it matches ``name[extras](version-spec)?(;
|
|
926
|
+
marker)?`` in full, with no direct-reference syntax — this is a POSITIVE
|
|
927
|
+
allowlist (see the module note above for why a denylist is the wrong
|
|
928
|
+
shape for this check). ``None`` covers every non-plain-PyPI form: a
|
|
929
|
+
direct reference (``name @ url``; any ``@``/``/``/``\\`` anywhere in the
|
|
930
|
+
spec fails the grammar), a VCS ref (``git+...`` — the ``+`` after
|
|
931
|
+
``git`` is not a legal name character, so this also fails the grammar), a
|
|
932
|
+
local path (bare ``.``/``..``, or anything containing a path separator),
|
|
933
|
+
a bare archive filename (:data:`_ARCHIVE_SUFFIXES`), or a leading-dash
|
|
934
|
+
pip option (``-e``, ``--no-index``, ``-r``).
|
|
935
|
+
|
|
936
|
+
Callers MUST treat ``None`` as "reject" — never as "safe" or "not
|
|
937
|
+
pinned": see :func:`is_platform_pinned` (fails closed, returns ``True``)
|
|
938
|
+
and ``simulo._client.runtime.Runtime.pip_install`` (raises
|
|
939
|
+
``SystemExit``) — the ONE validator both packages call (no duplicated
|
|
940
|
+
rule to drift out of sync).
|
|
941
|
+
"""
|
|
942
|
+
stripped = spec.strip()
|
|
943
|
+
if not stripped or stripped in (".", ".."):
|
|
944
|
+
return None
|
|
945
|
+
if stripped.startswith("-"):
|
|
946
|
+
return None
|
|
947
|
+
if stripped.lower().endswith(_ARCHIVE_SUFFIXES):
|
|
948
|
+
return None
|
|
949
|
+
match = _PLAIN_PYPI_SPEC.match(stripped)
|
|
950
|
+
if match is None:
|
|
951
|
+
return None
|
|
952
|
+
return re.sub(r"[-_.]+", "-", match.group("name").lower())
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
def is_platform_pinned(pkg_name: str) -> bool:
|
|
956
|
+
"""Return ``True`` if ``pkg_name`` must be denied as platform-pinned.
|
|
957
|
+
|
|
958
|
+
Reduces *pkg_name* to its canonical bare name via
|
|
959
|
+
:func:`canonical_pypi_name` and denies (returns ``True``) if:
|
|
960
|
+
|
|
961
|
+
* *pkg_name* is not a well-formed, plain-PyPI requirement at all — a
|
|
962
|
+
direct reference, VCS ref, local path, archive filename, or pip
|
|
963
|
+
option. :func:`canonical_pypi_name` returns ``None`` for all of these;
|
|
964
|
+
this function fails CLOSED on ``None`` (denies) rather than treating
|
|
965
|
+
an unparseable spec as "not pinned" — an unverifiable spec could
|
|
966
|
+
install arbitrary content under a pinned package's name.
|
|
967
|
+
* the canonical name matches the CUDA-wheel shape (see
|
|
968
|
+
:data:`_NVIDIA_CUDA_WHEEL_PATTERN`): ``nvidia-<lib>-cu<N>`` is denied
|
|
969
|
+
for any CUDA major ``<N>``, while nvidia helper packages outside that
|
|
970
|
+
shape (``nvidia-ml-py``, ``nvidia-dali-cuda120``) are allowed.
|
|
971
|
+
* the canonical name is an exact (not prefix) member of
|
|
972
|
+
:data:`PLATFORM_PINNED_PACKAGES` — ``"torchmetrics"`` and
|
|
973
|
+
``"simulo-console"`` do NOT match ``"torch"`` / ``"simulo"``.
|
|
974
|
+
|
|
975
|
+
Matching is case-insensitive and tolerant of the forms a real
|
|
976
|
+
``pip_install()`` call can pass — extras, version specifiers (bare or
|
|
977
|
+
parenthesized), and environment markers are all stripped by
|
|
978
|
+
:func:`canonical_pypi_name` before comparison, so ``"Torch"``,
|
|
979
|
+
``"torch(==2.1)"``, ``"simulo_interfaces"``, and
|
|
980
|
+
``"Simulo-Interfaces==0.5.0"`` all match their canonical entry.
|
|
981
|
+
|
|
982
|
+
This function is the client-side fast-fail check only (see the module
|
|
983
|
+
note above) — a package that passes it may still collide with the base
|
|
984
|
+
image; the backend's install-time check against the real base-env
|
|
985
|
+
install set is authoritative.
|
|
986
|
+
"""
|
|
987
|
+
normalized = canonical_pypi_name(pkg_name)
|
|
988
|
+
if normalized is None:
|
|
989
|
+
return True
|
|
990
|
+
if _NVIDIA_CUDA_WHEEL_PATTERN.match(normalized):
|
|
991
|
+
return True
|
|
992
|
+
return normalized in PLATFORM_PINNED_PACKAGES
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/callbacks.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/domain.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/platform/volume.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/__init__.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/anomaly.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/components.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/scenario.py
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/tensors.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo/interfaces/runtime/trainer.py
RENAMED
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo_interfaces.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo_interfaces.egg-info/requires.txt
RENAMED
|
File without changes
|
{simulo_interfaces-0.5.0 → simulo_interfaces-0.6.0}/src/simulo_interfaces.egg-info/top_level.txt
RENAMED
|
File without changes
|