exlab_wizard.plugins#

Plugins package. Backend Spec §6.

Re-exports the public plugin contract surface so plugin authors can write from exlab_wizard.plugins import Plugin, PluginContext, FileChange, PluginError, PluginInputRequired without reaching into private modules. The host- and worker-side machinery (host, _worker, logger) remains namespaced under the package and is not part of the plugin-author public API.

class exlab_wizard.plugins.FileChange(path, kind, summary, detail=<factory>)[source]#

Bases: object

A single mutation a plugin would make. Used by describe_changes.

Backend Spec §6.1.3.

path#

Absolute path under the rendered destination directory.

kind#

One of "modify", "create", "rename", "delete".

summary#

One-line human-readable description for UI display.

detail#

Optional structured payload for richer dry-run previews (e.g. {"writes": [{"cell": "B7", "value": "asmith"}]}).

Parameters:
detail: dict[str, Any]#
kind: str#
path: Path#
summary: str#
class exlab_wizard.plugins.HostPluginLogger(name='exlab_wizard.plugins')[source]#

Bases: PluginLogger

In-process forwarder used when plugins run without subprocess isolation.

Used by the host’s unit-test shim, by --no-isolation debug invocations of the plugin CLI (Backend Spec §6.10), and by the host when re-emitting parsed worker frames. Routes through exlab_wizard.logging.get_logger() so records flow into the canonical handler chain (per-equipment file, central rotating, stderr).

Parameters:

name (str)

debug(message, **fields)[source]#

Emit a DEBUG-level structured log record.

Parameters:
Return type:

None

error(message, **fields)[source]#

Emit an ERROR-level structured log record.

Parameters:
Return type:

None

info(message, **fields)[source]#

Emit an INFO-level structured log record.

Parameters:
Return type:

None

warning(message, **fields)[source]#

Emit a WARNING-level structured log record.

Parameters:
Return type:

None

class exlab_wizard.plugins.Plugin[source]#

Bases: ABC

Abstract base class for all ExLab-Wizard plugins.

Backend Spec §6.1.3.

Lifecycle (one instance per creation session, all in the worker subprocess):

  • __init__() – cheap construction; no I/O.

  • validate_variables(variables) – called once at registration in a short-lived validation worker.

  • pre_transform_all(ctx) – called once before the file loop.

  • For each matched file:

    • can_handle(file, variables) – cheap predicate.

    • describe_changes(file, ctx) – only invoked in dry-run mode.

    • transform(file, ctx) – the actual mutation.

  • post_transform_all(ctx) – called once after the file loop.

  • on_plugin_failure(exc, ctx) – called only if any other hook raised.

api_version: ClassVar[str] = '1'#
abstractmethod can_handle(file_path, variables)[source]#

Secondary filter, called after the extension match.

Cheap and side-effect-free. Returning False means this file is skipped for this plugin only – other plugins still get a chance.

Parameters:
Return type:

bool

describe_changes(file_path, ctx)[source]#

Return the dry-run preview for what transform would do.

Default returns a single "modify" FileChange with no detail; plugins should override when the user-facing preview matters (e.g. listing the cells that would be written).

Parameters:
Return type:

list[FileChange]

name: ClassVar[str]#
on_plugin_failure(exc, ctx)[source]#

Called if any prior hook raised. Default: no-op.

Use to roll back partial state (delete a half-written sidecar file, restore a backup, close a leaked handle). The exception that caused the failure is passed in; the plugin MUST NOT re-raise. Returning normally means cleanup succeeded; raising means the cleanup itself failed and will be logged separately.

Parameters:
Return type:

None

optional_variables: ClassVar[list[str]] = []#
post_transform_all(ctx)[source]#

Called once after the file loop. Default: no-op.

Symmetric to pre_transform_all() – close handles, flush buffers, etc. Not called if pre_transform_all itself raised.

Parameters:

ctx (PluginContext)

Return type:

None

pre_transform_all(ctx)[source]#

Called once before the file loop. Default: no-op.

Use for batch setup that should be paid once per session (e.g. opening a workbook, opening a DB connection). State stored on self is preserved through the loop because the worker holds one instance for the whole session.

Parameters:

ctx (PluginContext)

Return type:

None

required_variables: ClassVar[list[str]] = []#
supported_extensions: ClassVar[list[str]]#
abstractmethod transform(file_path, ctx)[source]#

Mutate file_path in place.

On unrecoverable failure raise PluginError with a human-readable message. On a discovered need for additional input, raise PluginInputRequired. Return value is ignored.

Parameters:
Return type:

None

validate_variables(variables)[source]#

Return a list of error strings; empty list means “valid”.

Default implementation reports any of self.required_variables that are missing from variables or are present but empty. Plugin authors override only to add bespoke checks (date-format, equipment-allowlist, etc.); the override should call super() first to preserve the missing-required check.

Backend Spec §6.1.3 (variable validation in worker, not host).

Parameters:

variables (dict[str, Any])

Return type:

list[str]

version: ClassVar[str]#
class exlab_wizard.plugins.PluginContext(variables, dst_root, answers_file, template_name, template_version, run_kind, equipment_id, project, dry_run, log)[source]#

Bases: object

Read-only context handed to every plugin lifecycle hook.

Constructed once per creation session by the host and passed in across the IPC boundary. Plugins read from it but MUST NOT mutate it (the dataclass is frozen as a defensive measure – mutation attempts raise dataclasses.FrozenInstanceError).

Backend Spec §6.1.4.

Parameters:
answers_file: Path#
dry_run: bool#
dst_root: Path#
equipment_id: str#
log: PluginLogger#
project: str#
run_kind: str#
template_name: str#
template_version: str#
variables: dict[str, Any]#
exception exlab_wizard.plugins.PluginError[source]#

Bases: ExLabError

Raised by plugin workers to signal expected failure. Backend Spec §6.

exception exlab_wizard.plugins.PluginInputRequired(fields, reason)[source]#

Bases: ExLabError

Raised by plugin workers to escalate for additional input. Backend Spec §6.4.

Parameters:
property reason: str#
class exlab_wizard.plugins.PluginLogFrame(level: str, message: str, context: dict[str, ~typing.Any]=<factory>)[source]#

Bases: Struct

Wire format for worker-side log records.

The worker subprocess emits one PluginLogFrame-shaped JSON object per log line on its stderr stream; the host parses each line and forwards into the canonical logger chain. Backend Spec §6.3.2.

context: dict[str, Any]#
level: str#
message: str#
class exlab_wizard.plugins.PluginLogger[source]#

Bases: ABC

Abstract structured-log interface plugins receive on ctx.log.

Implementations forward to either the in-process stdlib logger (HostPluginLogger) or the worker stderr channel (WorkerPluginLogger). Both expose the same four-method shape; plugin authors should not reach behind it.

abstractmethod debug(message, **fields)[source]#

Emit a DEBUG-level structured log record.

Parameters:
Return type:

None

abstractmethod error(message, **fields)[source]#

Emit an ERROR-level structured log record.

Parameters:
Return type:

None

abstractmethod info(message, **fields)[source]#

Emit an INFO-level structured log record.

Parameters:
Return type:

None

abstractmethod warning(message, **fields)[source]#

Emit a WARNING-level structured log record.

Parameters:
Return type:

None

class exlab_wizard.plugins.WorkerPluginLogger(stream=None)[source]#

Bases: PluginLogger

Worker-side forwarder. Emits JSON frames on stderr.

Used inside the plugin worker subprocess (Backend Spec §6.3.1). Each call serializes a PluginLogFrame via msgspec.json and writes a single newline-terminated line to the configured stream (defaults to sys.stderr). The host reads these line-by-line and forwards them to the canonical logger.

The worker MUST NOT use stdout for log output – that channel is reserved for the IPC envelope. Stderr is the structured-log sideband.

Parameters:

stream (Optional[IO[str]])

debug(message, **fields)[source]#

Emit a DEBUG-level structured log record.

Parameters:
Return type:

None

error(message, **fields)[source]#

Emit an ERROR-level structured log record.

Parameters:
Return type:

None

info(message, **fields)[source]#

Emit an INFO-level structured log record.

Parameters:
Return type:

None

warning(message, **fields)[source]#

Emit a WARNING-level structured log record.

Parameters:
Return type:

None

Modules

base

Plugin contract base class.

host

Plugin host: spawns plugin worker subprocesses with strict isolation.

logger

Plugin logger shim.

registry

Plugin registry -- manifest-driven discovery + dispatch resolution.