EngineModuleLoader
What it is
EngineModuleLoader discovers module dependencies, computes a deterministic load order, and imports/instantiates enabled ABI modules from an EngineConfiguration. Each loaded module is constructed with an EngineProxy and a validated Pydantic configuration.
Public API
- Class:
EngineModuleLoader__init__(configuration: EngineConfiguration)- Stores engine configuration used to locate and configure modules.
modules -> Dict[str, BaseModule](property)- Returns loaded module instances keyed by module name.
module_load_order -> List[str](property)- Returns the computed module load order (topologically sorted).
ordered_modules -> List[BaseModule](property)- Returns loaded module instances in
module_load_order.
- Returns loaded module instances in
get_module_dependencies(module_name: str, scanned_modules: List[str] = []) -> Dict[str, ModuleDependencies]- Imports a configured module, calls
ABIModule.get_dependencies(), and recursively resolves dependencies. - Supports “soft” dependencies via the
#softsuffix (not required to be configured). - Detects circular dependency chains during dependency scanning.
- Imports a configured module, calls
get_modules_dependencies(module_names: List[str] = []) -> Dict[str, ModuleDependencies]- Resolves dependencies for all enabled modules in configuration, or only for the specified subset.
- Persists results internally for later loading.
load_modules(engine: IEngine, module_names: List[str] = []) -> Dict[str, BaseModule]- Computes dependency load order and loads modules in that order.
- For each enabled module:
- Imports
module_config.module - Validates
ABIModule.Configuration(must be a subclass ofModuleConfiguration) - Instantiates
ABIModule(EngineProxy(...), cfg) - Calls
module.on_load()
- Imports
Configuration/Dependencies
-
Configuration source
- Uses
EngineConfiguration.modulesentries; each entry must have:module(import path string)enabled(bool)config(dict passed intoABIModule.Configuration(...)along withglobal_config)
- Uses
EngineConfiguration.global_configwhen building each module configuration.
- Uses
-
Module contract
- Importable module must expose:
ABIModuleclassABIModule.get_dependencies() -> ModuleDependenciesABIModule.Configurationclass (must be a subclass ofModuleConfiguration)
ABIModule(...)instance must be aBaseModuleand must implementon_load().
- Importable module must expose:
-
Runtime dependencies
importlibfor module importingpydantic_corefor configuration validation errorsEngineProxy,IEngine,BaseModule,ModuleConfiguration,ModuleDependencies
Usage
from naas_abi_core.engine.engine_loaders.EngineModuleLoader import EngineModuleLoader
from naas_abi_core.engine.engine_configuration.EngineConfiguration import EngineConfiguration
# engine: IEngine must be provided by your runtime.
# configuration: EngineConfiguration must contain module entries with module path, enabled flag, and config dict.
loader = EngineModuleLoader(configuration)
modules = loader.load_modules(engine)
# Access loaded modules
print(loader.module_load_order)
for m in loader.ordered_modules:
print(type(m))Caveats
- Dependency rules
- Non-soft dependencies (
"dep") must be present in the enabled dependency graph; otherwise loading aborts withValueError. - Soft dependencies (
"dep#soft") may be absent from configuration; they are ignored if not configured.
- Non-soft dependencies (
- Cycle handling
- Topological sort raises
ValueErroron circular dependencies. module_dependencies_recursive()is explicitly not protected against cycles and should only be used after a successful topological sort.
- Topological sort raises
- Configuration validation
- Module configuration instantiation errors (Pydantic validation) are wrapped and raised as
ValueError.
- Module configuration instantiation errors (Pydantic validation) are wrapped and raised as
- Special-case proxy unlock
- The
EngineProxyis created withunlocked=Trueonly whenmodule_name == "naas_abi".
- The