Source code for simvx.core.script_embed
"""Virtual importlib finder/loader for embedded scripts.
Embedded scripts are source code stored directly in the scene/project file
(on ``node._script_embedded``) rather than as separate ``.py`` files. This
module installs a ``sys.meta_path`` finder that makes those scripts importable
via standard ``import`` machinery, so ScriptManager can load them the same way
as file-backed scripts.
Usage:
from simvx.core.script_embed import EmbeddedScriptFinder
EmbeddedScriptFinder.register("_simvx_embed_my_script", source_code)
import _simvx_embed_my_script # works!
EmbeddedScriptFinder.unregister("_simvx_embed_my_script")
"""
from __future__ import annotations
import importlib.abc
import importlib.machinery
import logging
import sys
log = logging.getLogger(__name__)
[docs]
class EmbeddedScriptFinder(importlib.abc.MetaPathFinder):
"""Finds modules registered as embedded scripts."""
_registry: dict[str, str] = {}
[docs]
@classmethod
def register(cls, module_name: str, source: str):
"""Register source code under *module_name* for import."""
cls._registry[module_name] = source
# Invalidate any cached "not found" result
sys.modules.pop(module_name, None)
[docs]
@classmethod
def unregister(cls, module_name: str):
"""Remove an embedded module registration."""
cls._registry.pop(module_name, None)
sys.modules.pop(module_name, None)
[docs]
def find_module(self, fullname, path=None):
if fullname in self._registry:
return _EmbeddedLoader()
return None
[docs]
def find_spec(self, fullname, path, target=None):
if fullname not in self._registry:
return None
return importlib.machinery.ModuleSpec(
fullname,
_EmbeddedLoader(),
origin=f"<embedded:{fullname}>",
)
class _EmbeddedLoader(importlib.abc.Loader):
"""Loads embedded script source via exec()."""
def create_module(self, spec):
return None # use default semantics
def exec_module(self, module):
source = EmbeddedScriptFinder._registry.get(module.__name__, "")
code = compile(source, f"<embedded:{module.__name__}>", "exec")
exec(code, module.__dict__)
# Install the finder once at import time
if not any(isinstance(f, EmbeddedScriptFinder) for f in sys.meta_path):
sys.meta_path.insert(0, EmbeddedScriptFinder())