simulo 0.26.0__py3-none-any.whl

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.
Files changed (55) hide show
  1. simulo/__init__.py +433 -0
  2. simulo/_client/__init__.py +6 -0
  3. simulo/_client/_entrypoint.py +313 -0
  4. simulo/_client/_mounts.py +25 -0
  5. simulo/_client/_runner.py +186 -0
  6. simulo/_client/_secure_downloads.py +1181 -0
  7. simulo/_client/app.py +1308 -0
  8. simulo/_client/asset.py +331 -0
  9. simulo/_client/asset_api.py +517 -0
  10. simulo/_client/asset_package.py +1103 -0
  11. simulo/_client/asset_pins.py +187 -0
  12. simulo/_client/builtin_aliases.py +107 -0
  13. simulo/_client/bundle.py +254 -0
  14. simulo/_client/cancel_api.py +104 -0
  15. simulo/_client/cli.py +9063 -0
  16. simulo/_client/config.py +186 -0
  17. simulo/_client/credentials.py +210 -0
  18. simulo/_client/discovery.py +214 -0
  19. simulo/_client/export_api.py +212 -0
  20. simulo/_client/export_bundle.py +296 -0
  21. simulo/_client/facades.py +581 -0
  22. simulo/_client/http.py +414 -0
  23. simulo/_client/identity_api.py +117 -0
  24. simulo/_client/install_samples.py +267 -0
  25. simulo/_client/jobs_api.py +224 -0
  26. simulo/_client/learning.py +393 -0
  27. simulo/_client/login.py +319 -0
  28. simulo/_client/mode.py +29 -0
  29. simulo/_client/outputs.py +116 -0
  30. simulo/_client/packaging.py +445 -0
  31. simulo/_client/preflight_api.py +186 -0
  32. simulo/_client/preflight_render.py +200 -0
  33. simulo/_client/registry.py +98 -0
  34. simulo/_client/runtime.py +185 -0
  35. simulo/_client/runtime_display.py +90 -0
  36. simulo/_client/seed_ref.py +76 -0
  37. simulo/_client/stub.py +41 -0
  38. simulo/_client/submit_api.py +1057 -0
  39. simulo/_client/templates/__init__.py +21 -0
  40. simulo/_client/templates/inference/app.py.tmpl +316 -0
  41. simulo/_client/templates/inference/simuloignore.tmpl +30 -0
  42. simulo/_client/templates/scenario/app.py.tmpl +93 -0
  43. simulo/_client/templates/scenario/simuloignore.tmpl +27 -0
  44. simulo/_client/templates/training/app.py.tmpl +235 -0
  45. simulo/_client/templates/training/simuloignore.tmpl +29 -0
  46. simulo/_client/view_fragment.py +21 -0
  47. simulo/_client/view_session_api.py +122 -0
  48. simulo/_client/volume.py +71 -0
  49. simulo/callbacks.py +274 -0
  50. simulo/py.typed +0 -0
  51. simulo-0.26.0.dist-info/METADATA +130 -0
  52. simulo-0.26.0.dist-info/RECORD +55 -0
  53. simulo-0.26.0.dist-info/WHEEL +5 -0
  54. simulo-0.26.0.dist-info/entry_points.txt +2 -0
  55. simulo-0.26.0.dist-info/top_level.txt +1 -0
simulo/__init__.py ADDED
@@ -0,0 +1,433 @@
1
+ """Simulo thin client — the user-facing, torch-free SDK surface.
2
+
3
+ This is the ONE Simulo distribution that ships ``simulo/__init__.py``. The
4
+ ``extend_path`` call below unions every ``simulo/`` directory on ``sys.path`` into
5
+ this package's ``__path__`` so the PEP 420 namespace portions shipped by sibling
6
+ distributions (``simulo.core`` / ``simulo.backend`` / ``simulo.recording`` from
7
+ ``simulo-backend``; ``simulo.interfaces`` from ``simulo-interfaces``) resolve
8
+ alongside the clean top-level ``simulo.App`` re-exported here.
9
+
10
+ It MUST run before the re-exports below, because importing the thin client's
11
+ internal ``App`` module pulls in ``simulo.interfaces`` (a sibling distribution).
12
+
13
+ **The one-import rule.** Everything a user can legitimately need is reachable
14
+ from ``import simulo``. ``simulo.interfaces`` is a packaging detail two
15
+ distributions share so the thin client and the backend hold ONE definition
16
+ instead of drifting copies — it is never a namespace a user types. A user who
17
+ can write ``simulo.Physics.rigid(...)`` must not have to discover a second
18
+ import to write ``except simulo.JobFailedError`` or
19
+ ``collision=simulo.Collision.enabled()``.
20
+
21
+ The rule is enforced as a rule, not as a hand-kept list: the thin client's
22
+ one-import-rule test derives the expected surface from
23
+ ``simulo.interfaces.__all__`` and ``simulo.interfaces.authoring.__all__`` and
24
+ fails on any name that is not reachable here or explicitly declared in that
25
+ test's exclusions table with a reason. A hand-maintained whitelist is only ever
26
+ as current as the last person who remembered to edit it, and two names were
27
+ missed that way before the rule replaced it.
28
+
29
+ Three kinds of name live on ``simulo``:
30
+
31
+ * **Platform** names — ``App / Runtime / Volume / Asset / Output /
32
+ OutputKind / SystemType / callbacks`` — are **real, eager** attributes bound
33
+ below. They are always torch-free (``Output`` is the ``simulo-interfaces``
34
+ contract dataclass, ``OutputKind`` and ``SystemType`` its wire-stable
35
+ ``StrEnum``\\ s, all three re-exported).
36
+ * **Contract vocabulary** — the shared error hierarchy (``SimuloError`` and its
37
+ subclasses), the ``NewType`` identifier aliases (``JobId``, ``ProjectId``, …),
38
+ and the authoring value types that are not already carried by the lazy
39
+ resolver (``Collision``, ``Transform``, the primitive shapes, the factory
40
+ return types, the markers, …) — are also **real, eager** attributes. They cost
41
+ nothing to bind: ``simulo.interfaces.authoring`` is a zero-dependency package
42
+ that the thin client's own learning resolver already imports at module scope,
43
+ so this adds no import and no dependency. Errors and IDs are likewise
44
+ stdlib-only (``NewType`` is the underlying ``str`` at runtime).
45
+ * **Lazy, mode-aware** names — the learning names ``Task / Scene / Robot /
46
+ LearningEnv / RLTrainer / …`` plus the execution-time ``save_output`` and
47
+ ``run`` (the scenario execution loop) — are
48
+ resolved through the PEP 562 ``__getattr__`` below, which delegates to the
49
+ thin client's internal learning-name resolver. At submit (discovery mode) they resolve to
50
+ lean stand-ins so the user app imports without the heavy runtime; only the
51
+ backend runner (execution mode) resolves them to the real implementations.
52
+ Because the platform names are real attributes, ``__getattr__`` fires *only*
53
+ for the lazy names. The RELOCATED authoring value types (``Pose`` /
54
+ ``Material`` / ``Physics`` / ``Light`` / ``Terrain`` / ``World`` /
55
+ ``Entity`` / ``Visual`` / ``RecordConfig``) ride the same resolver but
56
+ resolve to their real, dependency-free ``simulo.interfaces.authoring``
57
+ class in BOTH modes — they need no heavy runtime, and exactly one
58
+ definition exists. (``Terrain`` is ground you generate; ``World`` is a
59
+ prebuilt place you bring, ``World.usd(...)``.)
60
+ """
61
+
62
+ import pkgutil as _pkgutil
63
+ from typing import TYPE_CHECKING
64
+
65
+ __path__ = _pkgutil.extend_path(__path__, __name__)
66
+
67
+ from simulo import callbacks
68
+ from simulo._client.app import App
69
+ from simulo._client.asset import Asset
70
+ from simulo._client.learning import LEARNING_NAMES as _LEARNING_NAMES
71
+ from simulo._client.learning import resolve as _resolve_learning
72
+ from simulo._client.runtime import Runtime
73
+ from simulo._client.volume import Volume
74
+
75
+ # --- Contract vocabulary, re-exported under the one-import rule ---------------
76
+ #
77
+ # The shared error hierarchy and the identifier aliases. Both are stdlib-only:
78
+ # the exceptions derive from ``Exception``, and every identifier is a
79
+ # ``typing.NewType`` over ``str`` (zero runtime cost, distinct to a type
80
+ # checker). A user catching a failed job writes ``except simulo.JobFailedError``,
81
+ # never a second import.
82
+ from simulo.interfaces import (
83
+ CheckpointId,
84
+ ContractViolationError,
85
+ Digest,
86
+ ImportResolutionError,
87
+ JobFailedError,
88
+ JobId,
89
+ JobPublicId,
90
+ ModelPublicId,
91
+ OrganizationId,
92
+ OutputPublicId,
93
+ PackageId,
94
+ ProjectId,
95
+ RecordingPublicId,
96
+ ResourceId,
97
+ ResourceNotFoundError,
98
+ ResourceUri,
99
+ ResumeIncompatibleError,
100
+ SimuloError,
101
+ TagId,
102
+ TrainedModelId,
103
+ parse_job_public_id,
104
+ parse_model_public_id,
105
+ parse_output_public_id,
106
+ parse_recording_public_id,
107
+ )
108
+
109
+ # The authoring value types that the lazy resolver does NOT already carry.
110
+ # Every one is a real, dependency-free class with exactly one definition, so —
111
+ # unlike the learning names — there is nothing mode-aware about them and no
112
+ # reason to defer: binding them eagerly is what makes ``simulo.Collision`` and
113
+ # ``simulo.Transform`` exist at all.
114
+ #
115
+ # ``simulo.Asset`` (the thin client's registry HANDLE, imported above — the
116
+ # name every shipped example and doc page uses) and
117
+ # ``simulo.interfaces.authoring.Asset`` (the model-source spec) used to share
118
+ # one name — two different user-facing
119
+ # classes, both exposing ``.usd()`` / ``.urdf()``, so re-exporting the
120
+ # authoring one here would have silently shadowed the handle users actually
121
+ # want. #693 (owner decision) resolved the collision by renaming the
122
+ # contract-side class to ``AssetSource``; with the names distinct, it is
123
+ # re-exported here like every other concrete authoring type — no exclusion
124
+ # needed any more.
125
+ from simulo.interfaces.authoring import (
126
+ ArrowMarker,
127
+ AssetEntity,
128
+ AssetSource,
129
+ AxesMarker,
130
+ Collision,
131
+ Cone,
132
+ Cuboid,
133
+ Cylinder,
134
+ DeformableMaterial,
135
+ DeformablePhysics,
136
+ DistantLight,
137
+ DomeLight,
138
+ GroundPlane,
139
+ LightConfig,
140
+ PointMarker,
141
+ PrimitiveShape,
142
+ RigidPhysics,
143
+ RoughTerrain,
144
+ Sphere,
145
+ SphereMarker,
146
+ SubTerrainConfig,
147
+ SurfaceMaterial,
148
+ TerrainConfig,
149
+ Transform,
150
+ URDFAsset,
151
+ USDAsset,
152
+ USDWorld,
153
+ VisualMarkerBase,
154
+ )
155
+ from simulo.interfaces.platform import Output, OutputKind, SystemType
156
+
157
+ # Public surface = eager platform exports (real attributes; ``__getattr__`` never
158
+ # fires for these) + the lazily-resolved learning names. Kept a static literal so
159
+ # tooling recognises the re-exports; a test guards it against ``LEARNING_NAMES`` drift.
160
+ __all__ = [
161
+ # platform (eager, real)
162
+ "App",
163
+ "Output",
164
+ "OutputKind",
165
+ "SystemType",
166
+ "Runtime",
167
+ "Volume",
168
+ "Asset",
169
+ "callbacks",
170
+ # contract errors (eager, real) — the shared exception hierarchy
171
+ "SimuloError",
172
+ "ContractViolationError",
173
+ "ImportResolutionError",
174
+ "ResourceNotFoundError",
175
+ "ResumeIncompatibleError",
176
+ "JobFailedError",
177
+ # contract identifiers (eager, real) — NewType aliases over ``str``
178
+ "OrganizationId",
179
+ "ProjectId",
180
+ "PackageId",
181
+ "JobId",
182
+ "JobPublicId",
183
+ "ModelPublicId",
184
+ "RecordingPublicId",
185
+ "OutputPublicId",
186
+ "parse_job_public_id",
187
+ "parse_model_public_id",
188
+ "parse_recording_public_id",
189
+ "parse_output_public_id",
190
+ "ResourceId",
191
+ "CheckpointId",
192
+ "TrainedModelId",
193
+ "TagId",
194
+ "ResourceUri",
195
+ "Digest",
196
+ # authoring value types (eager, real) — the rest of the vocabulary the
197
+ # lazy resolver below does not carry
198
+ "Collision",
199
+ "RigidPhysics",
200
+ "DeformableMaterial",
201
+ "DeformablePhysics",
202
+ "SurfaceMaterial",
203
+ "AssetSource",
204
+ "USDAsset",
205
+ "URDFAsset",
206
+ "LightConfig",
207
+ "DistantLight",
208
+ "DomeLight",
209
+ "TerrainConfig",
210
+ "GroundPlane",
211
+ "SubTerrainConfig",
212
+ "RoughTerrain",
213
+ "USDWorld",
214
+ "PrimitiveShape",
215
+ "Cone",
216
+ "Cuboid",
217
+ "Sphere",
218
+ "Cylinder",
219
+ "Transform",
220
+ "AssetEntity",
221
+ "VisualMarkerBase",
222
+ "AxesMarker",
223
+ "SphereMarker",
224
+ "ArrowMarker",
225
+ "PointMarker",
226
+ # lazy, mode-aware (the learning names + save_output)
227
+ "ActuatorGainsConfig",
228
+ "Camera",
229
+ "CameraSpawnConfig",
230
+ "ContactSensor",
231
+ "DifferentialIKController",
232
+ "Entity",
233
+ "ForceTorqueSensor",
234
+ "FrameTransformer",
235
+ "FrameTransformerTarget",
236
+ "GripperCommand",
237
+ "GripperState",
238
+ "IMU",
239
+ "LearningEnv",
240
+ "Light",
241
+ "Material",
242
+ "ObservationComponent",
243
+ "ObservationSet",
244
+ "OperationalSpaceController",
245
+ "ParallelGripperActuator",
246
+ "Physics",
247
+ "Player",
248
+ "Policy",
249
+ "Pose",
250
+ "Prop",
251
+ "RLPlayer",
252
+ "RLTrainer",
253
+ "RayCaster",
254
+ "RayPattern",
255
+ "RecordConfig",
256
+ "RewardComponent",
257
+ "RewardSet",
258
+ "Robot",
259
+ "Scenario",
260
+ "Scene",
261
+ "SensorOffset",
262
+ "Simulation",
263
+ "SurfaceGripperActuator",
264
+ "Task",
265
+ "Terrain",
266
+ "TerminationCondition",
267
+ "TerminationSet",
268
+ "Trainer",
269
+ "Visual",
270
+ "World",
271
+ "run",
272
+ "save_output",
273
+ ]
274
+
275
+
276
+ def __getattr__(name: str) -> object:
277
+ """PEP 562 hook: lazily resolve the mode-aware learning names.
278
+
279
+ Fires only for attributes missing from the module dict — i.e. never for the
280
+ real platform exports above. Delegates the learning names to the thin
281
+ client's internal resolver (discovery stand-in vs. real
282
+ ``simulo.core`` class, by mode); everything else is a genuine ``AttributeError``.
283
+ """
284
+ if name in _LEARNING_NAMES:
285
+ return _resolve_learning(name)
286
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
287
+
288
+
289
+ def __dir__() -> list[str]:
290
+ """Expose only the public surface to ``dir(simulo)``.
291
+
292
+ That is ``__all__`` (eager platform exports + learning names) unioned with
293
+ :data:`LEARNING_NAMES`. Private module globals (``_pkgutil``,
294
+ ``_resolve_learning``, ``_LEARNING_NAMES``, the ``TYPE_CHECKING`` import, …)
295
+ are deliberately filtered out so tab-completion shows the user-facing API only.
296
+ """
297
+ return sorted(set(__all__) | set(_LEARNING_NAMES))
298
+
299
+
300
+ if TYPE_CHECKING:
301
+ # Static aliases so a type checker sees the learning names with precise types
302
+ # (PEP 562 ``__getattr__`` would otherwise type them as ``Any``). The rule that
303
+ # this block MUST follow — and that the mypy positive test in
304
+ # ``tests/test_learning_typing.py`` locks — is:
305
+ #
306
+ # * **Subclassable** names (used as ``class X(simulo.Task)``) alias their
307
+ # ``interfaces.runtime`` Protocol, so the subclass is checked against the
308
+ # contract (e.g. ``TaskProtocol``).
309
+ # * **Instantiated mirror** names (used as ``simulo.LearningEnv(task=...)``,
310
+ # ``simulo.RLTrainer(env=...)``, ``simulo.Robot(...)``,
311
+ # ``simulo.RLPlayer(env=...)``) alias their typing FACADE in the thin
312
+ # client's internal facades module — a Protocol alias would make mypy
313
+ # reject instantiation ("Cannot instantiate protocol class"), and
314
+ # ``Any`` (the pre-Wave-C4 rule) type-checked nothing. The facade
315
+ # declares the contract surface plus the real constructor, so both the
316
+ # call and every member access are checked against the real member set
317
+ # and signature shape (name, kind, arity, presence of a default —
318
+ # NOT the declared type; see the facades module docstring for the
319
+ # known gap); facade fidelity to the contract AND to the concrete
320
+ # 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
+ # * **Annotation-position mirror** names alias their contract Protocol:
331
+ # ``Scene`` is ``SceneProtocol`` — measured across every shipped user
332
+ # surface it appears only as ``def build(self, scene: simulo.Scene)``,
333
+ # never instantiated, and the Protocol alias is what keeps that override
334
+ # Liskov-compatible with the contract's ``scene: SceneProtocol``.
335
+ # * **Relocated value types** alias their real, dependency-free
336
+ # ``simulo.interfaces.authoring`` class — precise field checking in both
337
+ # modes, because only one definition exists.
338
+ #
339
+ # Engine authoring vocabulary (the one-import rule's second half —
340
+ # ``Camera`` and its sensor siblings, ``Simulation``, the controllers, the
341
+ # actuators, the ``*Set`` containers): only ``Camera`` and ``Prop`` get an
342
+ # alias here. Both have a contract Protocol in
343
+ # ``simulo.interfaces.authoring`` (``CameraProtocol`` / ``PropProtocol``)
344
+ # and both are instantiated, never subclassed (``simulo.Camera(width=640,
345
+ # ...)``, ``simulo.Prop(shape)``), so each follows the "instantiated mirror
346
+ # -> facade" rule above exactly. The other 19 names
347
+ # have no TYPE_CHECKING alias, DELIBERATELY, per the rule's own "where none
348
+ # exists, do not invent one" corollary:
349
+ # - ``CameraSpawnConfig`` / ``SensorOffset`` / ``RayPattern`` /
350
+ # ``ActuatorGainsConfig`` / ``GripperState`` / ``GripperCommand`` /
351
+ # ``Simulation`` have no contract Protocol at all — nothing to alias
352
+ # against.
353
+ # - ``ContactSensor`` / ``RayCaster`` / ``IMU`` / ``ForceTorqueSensor`` /
354
+ # ``FrameTransformer`` conform only to the BASE ``SensorProtocol``
355
+ # (path/update_period/enabled/debug_vis/name/update/reset) — it does not
356
+ # declare their type-specific readers (``read_net_forces``,
357
+ # ``read_hits``, ``read_linear_acceleration``, …), so aliasing them to
358
+ # ``SensorProtocol`` would be a LOSSY, misleading type (mypy would flag
359
+ # real shipped calls to those readers as errors) and aliasing to a
360
+ # Protocol at all breaks instantiation ("Cannot instantiate protocol
361
+ # class") — the same reason facades exist. Their own per-type contracts
362
+ # are a later decision (``simulo.interfaces.authoring.sensor``'s own
363
+ # docstring), not implied here.
364
+ # - ``ObservationSet`` / ``RewardSet`` / ``TerminationSet`` /
365
+ # ``DifferentialIKController`` / ``OperationalSpaceController`` /
366
+ # ``SurfaceGripperActuator`` / ``ParallelGripperActuator`` have no
367
+ # dedicated Protocol either.
368
+ # Runtime resolution (the lazy resolver) is unaffected either way — these
369
+ # fall back to ``__getattr__``'s ``-> object`` return type for a type
370
+ # checker, same as any name with no static override.
371
+ from simulo._client.facades import Camera as Camera
372
+ from simulo._client.facades import LearningEnv as LearningEnv
373
+ from simulo._client.facades import Prop as Prop
374
+ from simulo._client.facades import RLPlayer as RLPlayer
375
+ from simulo._client.facades import RLTrainer as RLTrainer
376
+ from simulo._client.facades import Robot as Robot
377
+ from simulo.interfaces.authoring import SceneProtocol
378
+ from simulo.interfaces.runtime import (
379
+ ObservationComponentProtocol,
380
+ PlayerProtocol,
381
+ PolicyProtocol,
382
+ RewardComponentProtocol,
383
+ ScenarioProtocol,
384
+ TaskProtocol,
385
+ TerminationConditionProtocol,
386
+ TrainerProtocol,
387
+ )
388
+
389
+ # Subclassable names alias their Protocol so ``class X(simulo.Task)`` is checked
390
+ # against ``TaskProtocol``.
391
+ Task = TaskProtocol
392
+ Scenario = ScenarioProtocol
393
+ Policy = PolicyProtocol
394
+ Player = PlayerProtocol
395
+ Trainer = TrainerProtocol
396
+ ObservationComponent = ObservationComponentProtocol
397
+ RewardComponent = RewardComponentProtocol
398
+ TerminationCondition = TerminationConditionProtocol
399
+ # Annotation-position mirror name (see the rule above).
400
+ Scene = SceneProtocol
401
+ # ``save_output`` aliases its real execution-mode implementation (a
402
+ # torch-free thin-client function), so ``simulo.save_output("summary",
403
+ # 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
+ from simulo._client.outputs import save_output as save_output
419
+
420
+ # RELOCATED authoring value types (Plan C — Waves C1, C2, then C4's
421
+ # ``RecordConfig``): the runtime object IS the real, dependency-free
422
+ # ``simulo.interfaces.authoring`` class in both
423
+ # modes, so these alias the real class — precise field/signature checking
424
+ # (e.g. mypy rejects ``simulo.Pose(xyz="nope")``) instead of ``Any``.
425
+ from simulo.interfaces.authoring import Entity as Entity
426
+ from simulo.interfaces.authoring import Light as Light
427
+ from simulo.interfaces.authoring import Material as Material
428
+ from simulo.interfaces.authoring import Physics as Physics
429
+ from simulo.interfaces.authoring import Pose as Pose
430
+ from simulo.interfaces.authoring import RecordConfig as RecordConfig
431
+ from simulo.interfaces.authoring import Terrain as Terrain
432
+ from simulo.interfaces.authoring import Visual as Visual
433
+ from simulo.interfaces.authoring import World as World
@@ -0,0 +1,6 @@
1
+ """Internal implementation package for the Simulo thin client.
2
+
3
+ Nothing here is part of the public API. Users import from the top-level
4
+ ``simulo`` namespace (``simulo.App`` / ``simulo.Runtime`` / ``simulo.callbacks``);
5
+ these modules are the private machinery behind those re-exports.
6
+ """