refactor(orchestrator): extract ActivityDriver ABC + driver factory
Stage 4 of the realism migration. Lifts the driver Protocol into a
proper ABC with default plant_file/read_file methods (raise
NotImplementedError), and adds get_driver_for(action) so the
orchestrator worker can dispatch by action shape without isinstance
chains.
SSHDriver now inherits ActivityDriver and implements:
- plant_file: streams base64 via stdin (ARG_MAX-safe, mirrors
decnet.canary.planter; commit c17b9e0). Honours mtime via touch -d
so realism-planned files don't all stamp at wall-clock-now.
- read_file: docker exec cat with FileNotFoundError on rc=1, used by
the upcoming EditAction (stage 3b).
EmailDriver inherits ActivityDriver. Driver alias kept for back-compat
during the migration; removed once realism stages 5-7 land.
This commit is contained in:
@@ -1,5 +1,74 @@
|
||||
"""Activity drivers for the orchestrator (MVP: SSH only)."""
|
||||
from decnet.orchestrator.drivers.base import ActivityResult, Driver
|
||||
from decnet.orchestrator.drivers.ssh import SSHDriver
|
||||
"""Activity drivers for the orchestrator.
|
||||
|
||||
__all__ = ["ActivityResult", "Driver", "SSHDriver"]
|
||||
Concrete drivers register dispatch in :func:`get_driver_for`. Same
|
||||
lazy-import pattern as :mod:`decnet.canary.factory`: the import-time
|
||||
cost of :mod:`decnet.orchestrator.drivers` stays low for callers that
|
||||
only need :class:`ActivityResult` / :class:`ActivityDriver`.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from decnet.orchestrator.drivers.base import (
|
||||
ActivityDriver,
|
||||
ActivityResult,
|
||||
Driver,
|
||||
)
|
||||
from decnet.orchestrator.scheduler import Action, FileAction, TrafficAction
|
||||
|
||||
__all__ = [
|
||||
"ActivityDriver",
|
||||
"ActivityResult",
|
||||
"Driver",
|
||||
"SSHDriver",
|
||||
"get_driver_for",
|
||||
]
|
||||
|
||||
|
||||
def __getattr__(name: str): # pragma: no cover - import passthrough
|
||||
"""Lazy access to concrete drivers.
|
||||
|
||||
Avoids dragging the docker-exec / email-driver code into every
|
||||
consumer that only needs the ABC.
|
||||
"""
|
||||
if name == "SSHDriver":
|
||||
from decnet.orchestrator.drivers.ssh import SSHDriver
|
||||
return SSHDriver
|
||||
if name == "EmailDriver":
|
||||
from decnet.orchestrator.drivers.email import EmailDriver
|
||||
return EmailDriver
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
|
||||
def get_driver_for(action: Action) -> ActivityDriver:
|
||||
"""Return the concrete driver that handles *action*.
|
||||
|
||||
Stage 4 of the realism migration adds this seam so the orchestrator
|
||||
worker can dispatch by action type without an isinstance chain in
|
||||
``_one_tick``. Stage 5 wires the worker to call this function
|
||||
instead of holding a single ``SSHDriver`` instance.
|
||||
|
||||
The set of action shapes the orchestrator can plan grows with the
|
||||
migration:
|
||||
|
||||
* :class:`TrafficAction` / :class:`FileAction` → :class:`SSHDriver`
|
||||
* :class:`EmailAction` (post-stage-5) → ``EmailDriver``
|
||||
* :class:`EditAction` (post-stage-3b) → :class:`SSHDriver`
|
||||
"""
|
||||
# Lazy imports keep the side-effecting docker-exec / email-driver
|
||||
# modules out of every importer's graph.
|
||||
from decnet.orchestrator.drivers.ssh import SSHDriver
|
||||
|
||||
if isinstance(action, (TrafficAction, FileAction)):
|
||||
return SSHDriver()
|
||||
# EmailAction lands in stage 5; reachable only after that import is
|
||||
# added to scheduler. Importing inside the branch avoids a cycle
|
||||
# with realism.llm at module load time.
|
||||
try:
|
||||
from decnet.orchestrator.emailgen.scheduler import EmailAction
|
||||
except ImportError: # pragma: no cover - scheduler always exists
|
||||
EmailAction = None # type: ignore[assignment]
|
||||
if EmailAction is not None and isinstance(action, EmailAction):
|
||||
from decnet.orchestrator.drivers.email import EmailDriver
|
||||
return EmailDriver()
|
||||
raise TypeError(
|
||||
f"no driver registered for action type {type(action).__name__}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user