7e9ad8a78d
tracker-policy-pr / check-pr (pull_request) Successful in 13s
test / integration-docker (pull_request) Successful in 35s
test / unit (pull_request) Successful in 47s
lint / lint (push) Successful in 57s
test / integration-firecracker (pull_request) Successful in 3m35s
test / coverage (pull_request) Successful in 32s
test / publish-infra (pull_request) Has been skipped
Brings in main's 8 commits — #470 (backend-agnostic CI guards: BackendStatus enum, quiet status(), is_backend_available/is_backend_ready, tests/_backend.py skip_unless_backend) plus firecracker status() readiness checks (kvm/kernel/ dropbear/mke2fs). Reconciled #470's additions into the reorg'd backend structure: * BackendStatus enum + the status(*, quiet=) ABC signature -> backend/base.py * is_backend_available / is_backend_ready -> backend/selection.py * exposed all three through the lazy backend facade (__init__). Integration-test conflicts were our control_plane->orchestrator renames vs main's skip_unless_docker -> skip_unless_backend swap: kept our refactored import paths + main's guard. Repointed main's new is_backend_* tests to patch selection._backends (our moved home) instead of the facade. pyright: 0 errors. Full unit suite green (2272). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
116 lines
3.6 KiB
Python
116 lines
3.6 KiB
Python
"""The bottle-backend package: abstract contract + backend selection.
|
|
|
|
Thin by design — nothing framework-heavy is imported at package init, so
|
|
importing any `backend.*` submodule (e.g. `backend.docker.util`) doesn't drag
|
|
the manifest / egress / git-gate framework into memory. The public names are
|
|
re-exported lazily:
|
|
|
|
* the abstract contract (`BottleBackend`, `BottleSpec`, `Bottle`, …) lives in
|
|
`backend.base`;
|
|
* backend selection / enumeration (`get_bottle_backend`,
|
|
`enumerate_active_agents`, …) in `backend.selection`;
|
|
* the concrete backends and the freeze helpers in their own submodules.
|
|
|
|
`from bot_bottle.backend import X` resolves X on first access via `__getattr__`
|
|
and caches it at package level, so existing call-sites (and
|
|
`patch.object(backend_mod, X, …)`) keep working.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
if TYPE_CHECKING:
|
|
from .base import (
|
|
ActiveAgent,
|
|
BackendStatus,
|
|
Bottle,
|
|
BottleBackend,
|
|
BottleCleanupPlan,
|
|
BottleImages,
|
|
BottlePlan,
|
|
BottleSpec,
|
|
ExecResult,
|
|
)
|
|
from .selection import (
|
|
enumerate_active_agents,
|
|
get_bottle_backend,
|
|
has_backend,
|
|
is_backend_available,
|
|
is_backend_ready,
|
|
known_backend_names,
|
|
)
|
|
from .docker import DockerBottleBackend
|
|
from .firecracker import FirecrackerBottleBackend
|
|
from .macos_container import MacosContainerBottleBackend
|
|
from .freeze import CommitCancelled, Freezer, get_freezer
|
|
|
|
|
|
# Public name -> submodule that defines it. Contract types resolve from `base`,
|
|
# selection/enumeration from `selection`, the concrete backends + freeze helpers
|
|
# from their own subpackages.
|
|
_LAZY_MODULES: dict[str, str] = {
|
|
"BottleSpec": "base",
|
|
"BottlePlan": "base",
|
|
"BottleCleanupPlan": "base",
|
|
"ExecResult": "base",
|
|
"ActiveAgent": "base",
|
|
"Bottle": "base",
|
|
"BottleImages": "base",
|
|
"BottleBackend": "base",
|
|
"BackendStatus": "base",
|
|
"get_bottle_backend": "selection",
|
|
"known_backend_names": "selection",
|
|
"has_backend": "selection",
|
|
"is_backend_available": "selection",
|
|
"is_backend_ready": "selection",
|
|
"enumerate_active_agents": "selection",
|
|
"_print_vm_install_instructions": "selection",
|
|
"DockerBottleBackend": "docker",
|
|
"FirecrackerBottleBackend": "firecracker",
|
|
"MacosContainerBottleBackend": "macos_container",
|
|
"CommitCancelled": "freeze",
|
|
"Freezer": "freeze",
|
|
"get_freezer": "freeze",
|
|
}
|
|
|
|
|
|
def __getattr__(name: str) -> Any:
|
|
"""Lazily surface the package's public names from their submodules and
|
|
cache them at package level — so `from bot_bottle.backend import X` and
|
|
`patch.object(backend_mod, X, …)` keep working without importing the
|
|
framework (or every backend) at package-init time."""
|
|
mod = _LAZY_MODULES.get(name)
|
|
if mod is None:
|
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
from importlib import import_module
|
|
|
|
value = getattr(import_module(f"{__name__}.{mod}"), name)
|
|
globals()[name] = value
|
|
return value
|
|
|
|
|
|
__all__ = [
|
|
"ActiveAgent",
|
|
"BackendStatus",
|
|
"Bottle",
|
|
"BottleBackend",
|
|
"BottleCleanupPlan",
|
|
"BottleImages",
|
|
"BottlePlan",
|
|
"BottleSpec",
|
|
"ExecResult",
|
|
"CommitCancelled",
|
|
"Freezer",
|
|
"get_freezer",
|
|
"DockerBottleBackend",
|
|
"FirecrackerBottleBackend",
|
|
"MacosContainerBottleBackend",
|
|
"enumerate_active_agents",
|
|
"get_bottle_backend",
|
|
"has_backend",
|
|
"is_backend_available",
|
|
"is_backend_ready",
|
|
"known_backend_names",
|
|
]
|