923d44bc09
tracker-policy-pr / check-pr (pull_request) Successful in 15s
test / integration-docker (pull_request) Successful in 20s
lint / lint (push) Successful in 53s
test / unit (pull_request) Failing after 2m5s
test / integration-firecracker (pull_request) Successful in 3m27s
test / coverage (pull_request) Has been skipped
test / publish-infra (pull_request) Has been skipped
The three concrete-backend package inits eagerly imported their `*BottleBackend` (which pulls the whole framework), so importing any leaf under them (e.g. `backend.docker.util`) paid ~78 modules. Give each the same thin `__getattr__` treatment as the top-level backend package: re-export the public class(es) lazily on first access, with a TYPE_CHECKING block for checkers. `import bot_bottle.backend.docker.util` drops from 78 -> 6 bot_bottle modules; the docker/firecracker/macos-container packages themselves are ~3 each. All `from bot_bottle.backend.docker import DockerBottleBackend` call-sites and the lazy selection path keep working. This is what makes backend leaves cheap — and unblocks moving docker_cmd into backend/docker/util if we want. Full unit suite green (2243). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
57 lines
1.8 KiB
Python
57 lines
1.8 KiB
Python
"""Docker bottle backend.
|
|
|
|
The bulk of the implementation lives in sibling modules:
|
|
|
|
- util: thin Docker subprocess wrappers
|
|
- network: Docker network plumbing
|
|
- bottle_plan: DockerBottlePlan
|
|
- bottle_cleanup_plan: DockerBottleCleanupPlan
|
|
- bottle: DockerBottle handle
|
|
- prepare: host-side resolution into a DockerBottlePlan
|
|
- launch: bring-up + teardown context manager
|
|
- cleanup: orphan enumeration, removal, active listing
|
|
- backend: DockerBottleBackend façade wiring the above
|
|
- infra: DockerInfraService (the per-host infra container)
|
|
|
|
Thin by design: the public names are re-exported lazily via `__getattr__`, so
|
|
importing a leaf like `backend.docker.util` doesn't drag `DockerBottleBackend`
|
|
(and the whole framework it pulls) into memory.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
if TYPE_CHECKING:
|
|
from .backend import DockerBottleBackend
|
|
from .bottle import DockerBottle
|
|
from .bottle_cleanup_plan import DockerBottleCleanupPlan
|
|
from .bottle_plan import DockerBottlePlan
|
|
|
|
|
|
_LAZY_MODULES: dict[str, str] = {
|
|
"DockerBottleBackend": "backend",
|
|
"DockerBottle": "bottle",
|
|
"DockerBottleCleanupPlan": "bottle_cleanup_plan",
|
|
"DockerBottlePlan": "bottle_plan",
|
|
}
|
|
|
|
|
|
def __getattr__(name: str) -> Any:
|
|
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__ = [
|
|
"DockerBottle",
|
|
"DockerBottleBackend",
|
|
"DockerBottleCleanupPlan",
|
|
"DockerBottlePlan",
|
|
]
|