exlab_wizard.plugins.registry#
Plugin registry – manifest-driven discovery + dispatch resolution.
Backend Spec §6.2.
The registry scans two roots (bundled + lab) for plugin directories,
parses each manifest.yml without importing any plugin Python code,
validates the manifest against the spec’s schema (§6.1.2), enforces the
api_version gate (§6.2.1), enforces the network-opt-in gate
(§6.3.3), and exposes a dispatch surface (candidates_for) that
matches files to plugins by extension.
Lab plugins win on name collision with bundled plugins (§6.2.1.4); the collision is logged so operators can audit which copy is in effect.
This module is host-only: it does not import the plugin’s Plugin
class – that import is deferred to the worker subprocess to preserve
the §6.3 crash-isolation guarantee. PluginRecord.plugin_class is
therefore typed as type | None and is only populated by the
--no-isolation test path; production code reads the plugin via the
manifest + source path and lets the worker do the import.
Classes
|
Parsed |
|
One plugin paired with the files in the rendered tree it matches. |
|
One plugin in the registry. |
|
Manifest-driven plugin registry. |
|
Outcome of a |
- class exlab_wizard.plugins.registry.PluginManifest(name, version, author, description, supported_extensions, api_version, required_variables=<factory>, optional_variables=<factory>, isolation=<factory>)[source]#
Bases:
objectParsed
manifest.ymlcontents. Backend Spec §6.1.2.Mirrors the schema documented in the spec, with default values for optional blocks.
isolationis a normalized dict containingtimeout_seconds,memory_mb, andnetwork– the raw YAML is rejected if any of these exceed their hard caps.- Parameters:
- class exlab_wizard.plugins.registry.PluginPlan(record, matching_files)[source]#
Bases:
objectOne plugin paired with the files in the rendered tree it matches.
Built by
PluginRegistry.candidates_for()for each plugin whosesupported_extensionsmatched at least one file.- Parameters:
record (
PluginRecord)
- record: PluginRecord#
- class exlab_wizard.plugins.registry.PluginRecord(manifest, plugin_class, source_path, source_root)[source]#
Bases:
objectOne plugin in the registry.
Carries the parsed manifest, the source-on-disk plugin directory, and the source root identifier (
BUNDLEDorLAB) so the Settings UI can show where each plugin came from.plugin_classisNonein the production path (the host doesn’t import plugin code); it’s only populated when a caller explicitly requests in-process testing viaPluginRegistry--no-isolationhelpers (Backend Spec §6.10). Backend Spec §6.2.1.- Parameters:
manifest (
PluginManifest)source_path (
Path)source_root (
PluginSourceRoot)
- manifest: PluginManifest#
- source_root: PluginSourceRoot#
- class exlab_wizard.plugins.registry.PluginRegistry(bundled_dir, lab_dir, supported_api_versions=frozenset({'1'}), allow_network=False)[source]#
Bases:
objectManifest-driven plugin registry.
Constructed at host startup with the two configured plugin roots; one call to
reload()populates the in-memory record table by walking each root, parsing eachmanifest.yml, and applying the validation gates from §6.1.2 / §6.2.1.Lab plugins win on name collision with bundled plugins (§6.2.1.4); a structured INFO log entry records the override.
- Parameters:
- candidates_for(file_paths)[source]#
Resolve plugins that match the given files by extension.
For each registered plugin, collects the subset of
file_pathswhose suffix matches one of the plugin’ssupported_extensions. Plugins with at least one matching file appear in the returned list; the order is alphabetical by plugin name (the host applies template-declared ordering on top – see §6.2.3).- Parameters:
- Return type:
- get(name)[source]#
Return the record registered under
name, orNone.- Parameters:
name (
str)- Return type:
- reload()[source]#
Re-scan both plugin roots and rebuild the in-memory record table.
Bundled plugins are scanned first; lab plugins are scanned second and replace any same-named bundled record. Plugins that fail validation (missing manifest, malformed YAML, bad
api_version, excessiveisolationlimits, network-opt-in declined) are excluded from the table and added to the report’srejectedlist with a short reason string.Backend Spec §6.2.1.
- Return type:
- class exlab_wizard.plugins.registry.RegistryReport(loaded=<factory>, rejected=<factory>)[source]#
Bases:
objectOutcome of a
PluginRegistry.reload()pass.loadedis the list of plugin names that survived validation;rejectedis a list of(name, reason)tuples for plugins that were skipped, with the reason short enough for a single log line. Used by the Settings UI to surface a load summary.