refactor(bottles): Bottle becomes an ABC; DockerBottle inherits
test / run tests/run_tests.py (pull_request) Successful in 14s

Bottle was the only Protocol in an otherwise-ABC family
(BottlePlan, BottleCleanupPlan, BottlePlatform are all ABCs).
Convert to an ABC with abstract exec_claude / cp_in / close,
matching the rest of the hierarchy.

Rename _DockerBottle -> DockerBottle: the underscore was a
default-Python-private instinct that doesn't match the sibling
plan classes (DockerBottlePlan, DockerBottleCleanupPlan), all of
which are equally "only constructed by the platform" and yet
public-by-name.

Re-export DockerBottle from claude_bottle.bottles.docker.
This commit is contained in:
2026-05-10 23:32:33 -04:00
parent d28f0e6d9b
commit aaed390953
4 changed files with 18 additions and 10 deletions
+4 -2
View File
@@ -5,21 +5,23 @@ The bulk of the implementation lives in sibling modules:
- util: thin Docker subprocess wrappers
- bottle_plan: DockerBottlePlan
- bottle_cleanup_plan: DockerBottleCleanupPlan
- bottle: _DockerBottle handle
- bottle: DockerBottle handle
- platform: DockerBottlePlatform
This file only re-exports the platform class so
This file only re-exports the public names so
`from claude_bottle.bottles.docker import DockerBottlePlatform` keeps
working.
"""
from __future__ import annotations
from .bottle import DockerBottle
from .bottle_cleanup_plan import DockerBottleCleanupPlan
from .bottle_plan import DockerBottlePlan
from .platform import DockerBottlePlatform
__all__ = [
"DockerBottle",
"DockerBottleCleanupPlan",
"DockerBottlePlan",
"DockerBottlePlatform",
+4 -2
View File
@@ -1,4 +1,4 @@
"""_DockerBottle — concrete Bottle handle yielded by
"""DockerBottle — concrete Bottle handle yielded by
DockerBottlePlatform.launch.
Holds the container name plus the in-container prompt path so
@@ -10,8 +10,10 @@ from __future__ import annotations
import subprocess
from .. import Bottle
class _DockerBottle:
class DockerBottle(Bottle):
"""Concrete Bottle for Docker."""
def __init__(self, container: str, teardown, prompt_path_in_container: str | None):
+4 -4
View File
@@ -2,7 +2,7 @@
Methods:
.prepare(spec, stage_dir=...) -> DockerBottlePlan
.launch(plan) -> ContextManager[_DockerBottle]
.launch(plan) -> ContextManager[DockerBottle]
.prepare_cleanup() -> DockerBottleCleanupPlan
.cleanup(plan) -> None
.list_active() -> None
@@ -25,7 +25,7 @@ from ...env_resolve import env_resolve
from ...log import die, info
from .. import BottleCleanupPlan, BottlePlan, BottlePlatform, BottleSpec
from . import util as docker_mod
from .bottle import _DockerBottle
from .bottle import DockerBottle
from .bottle_cleanup_plan import DockerBottleCleanupPlan
from .bottle_plan import DockerBottlePlan
@@ -128,7 +128,7 @@ class DockerBottlePlatform(BottlePlatform):
)
@contextmanager
def launch(self, plan: BottlePlan) -> Iterator[_DockerBottle]:
def launch(self, plan: BottlePlan) -> Iterator[DockerBottle]:
"""Build, launch, and provision a Docker bottle. Teardown on exit."""
assert isinstance(plan, DockerBottlePlan), (
f"DockerBottlePlatform.launch expects DockerBottlePlan, "
@@ -187,7 +187,7 @@ class DockerBottlePlatform(BottlePlatform):
prompt_path = self._provision_container(plan, container)
bottle = _DockerBottle(container, teardown, prompt_path)
bottle = DockerBottle(container, teardown, prompt_path)
yield bottle
finally:
teardown()