simvx.core.scene_io.loader¶
Module names¶
A loaded scene is a real module, and its name is a pure function of its
resolved path: the same file always lands on the same :data:sys.modules key
and two different files never share one. The readable part of the name is the
path relative to the project the scene belongs to (the directory holding
simvx.toml) or, failing that, to the scene’s own directory; a digest of the
full resolved path follows it, so two projects that both contain
levels/forest.py stay apart. The name carries no dots: a dotted key whose
parent packages do not exist is one that :mod:pickle, and anything else
reaching for a class through __import__, cannot resolve.
What sits beside a file scene¶
python scene.py can import helpers from beside itself only because the
interpreter launcher puts the script’s directory on :data:sys.path, and no
import API reproduces that. Scenes are ordinary Python files that must load from
anywhere, so the loader supplies the same reach without the same cost. A
permanent :data:sys.path entry would make every neighbour of every scene
importable under its bare name for the rest of the process, which is how two
scenes in two directories end up sharing one helpers module; levels/a.py
and levels/b.py each keeping a util.py beside them is the most ordinary
project layout there is, and it has to load.
So each scene directory gets a synthetic package of its own, named for the
directory the way a scene module is named for its file. While a scene’s own
top-level code runs, a bare import helpers resolves to helpers.py in
that directory and the module is registered as a submodule of that package:
a/util.py and b/util.py are two modules with two names, and a class
defined in either carries a __module__ that still resolves. The bare name is
bound alongside it for the length of the exec, which is what makes a second
import of the same neighbour, and any import helpers.thing, find what the
first one loaded; it is unbound afterwards, so nothing outside the scene ever
sees it. Two scenes in the same directory share one set of neighbours, as they
would under python.
The reach lasts as long as the scene’s own top-level code, which is where a
scene states what it needs; a scene that defers import helpers to the inside
of a method is asking for it after the scene has run, and should import at
module level instead. Only Python sources and packages are found this way, not
extension modules.
Folder scenes need none of this: they are packages, their files import each
other relatively, and nothing about them goes on :data:sys.path either.
:data:sys.meta_path¶
The three finders below are installed on the first load, not on import, so a
process that never loads a scene has an untouched import system. Once installed
they stay: a scene module and its neighbours remain in :data:sys.modules for
the life of the process, there is no unload, and a submodule of either, or a
- func:
importlib.reloadof a scene, still has to resolve afterwards. All three are inert for every name outside the two scene prefixes, so what they cost a host process after that is one dict lookup per import miss.
Freshness¶
Loading the same unchanged file twice returns the same class, so a repeat load keeps object identity; a load after an edit compiles the new text. Both come from one rule: the compiled result is kept against a digest of the file’s bytes, and a hit means the source has not changed since it was compiled. The digest is of the content itself, not of size and modification time, because those cannot tell a rewrite apart from what it replaced when the write lands on the same byte count and the timestamp does not move, which is one save on any filesystem that keeps whole-second times. A scene is no fresher than the neighbours it imported, so those count towards its signature too, and an edited neighbour is dropped from
- data:
sys.modulesso that the reload runs it again. For a folder scene the signature covers every.pyunder the folder.
Runtime loading: import a scene .py file or folder and instantiate its primary Node.
load_scene is the canonical way to take a path on disk and return a live
- class:
~simvx.core.Nodetree. For editing the source, use- class:
SceneFile/ :class:SceneModule; this module is the runtime side.
Module Contents¶
Functions¶
Load a scene from a |
|
Import path as a module of its own, leaving :data: |
Data¶
API¶
- simvx.core.scene_io.loader.__all__¶
[‘import_file’, ‘load_scene’]
- simvx.core.scene_io.loader.load_scene(path: str | pathlib.Path) simvx.core.node.Node[source]¶
Load a scene from a
.pyfile or scene-module folder.File path → runs the file as a module of its own and instantiates the primary :class:
Nodesubclass. Modules beside the file are importable by bare name while it runs, as they would be underpython scene.py. Folder path → runs the folder as a package and instantiates the primary class declared in__init__.pyor, as a fallback, in<folder>/<folder>.py.The primary class is instantiated directly: a scene IS a
Nodesubclass in a.pyfile, so importing the module is all the “script loading” there is.Raises: ValueError: If the path holds no usable
Nodesubclass.
- simvx.core.scene_io.loader.import_file(path: str | pathlib.Path) types.ModuleType[source]¶
Import path as a module of its own, leaving :data:
sys.pathalone.The name is the same pure function of the path :func:
load_sceneuses, so a file reached either way is one module, and what sits beside it is importable by bare name while it runs. This is the import a tool wants when it needs the module rather than a node tree, such as watching a file for changes.A file already loaded is returned as it stands; use :func:
importlib.reloadon the result to run it again.