refactor(bottles): Bottle becomes an ABC; DockerBottle inherits
test / run tests/run_tests.py (pull_request) Successful in 14s
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:
@@ -31,7 +31,6 @@ from abc import ABC, abstractmethod
|
|||||||
from contextlib import AbstractContextManager
|
from contextlib import AbstractContextManager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Protocol
|
|
||||||
|
|
||||||
from ..log import die
|
from ..log import die
|
||||||
from ..manifest import Manifest
|
from ..manifest import Manifest
|
||||||
@@ -82,7 +81,7 @@ class BottleCleanupPlan(ABC):
|
|||||||
short-circuit before showing the y/N."""
|
short-circuit before showing the y/N."""
|
||||||
|
|
||||||
|
|
||||||
class Bottle(Protocol):
|
class Bottle(ABC):
|
||||||
"""Handle to a running bottle. Yielded by a platform's launch step.
|
"""Handle to a running bottle. Yielded by a platform's launch step.
|
||||||
|
|
||||||
`exec_claude` runs `claude` inside the bottle and blocks until the
|
`exec_claude` runs `claude` inside the bottle and blocks until the
|
||||||
@@ -92,8 +91,13 @@ class Bottle(Protocol):
|
|||||||
|
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def exec_claude(self, argv: list[str], *, tty: bool = True) -> int: ...
|
def exec_claude(self, argv: list[str], *, tty: bool = True) -> int: ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def cp_in(self, host_path: str, container_path: str) -> None: ...
|
def cp_in(self, host_path: str, container_path: str) -> None: ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
def close(self) -> None: ...
|
def close(self) -> None: ...
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,21 +5,23 @@ The bulk of the implementation lives in sibling modules:
|
|||||||
- util: thin Docker subprocess wrappers
|
- util: thin Docker subprocess wrappers
|
||||||
- bottle_plan: DockerBottlePlan
|
- bottle_plan: DockerBottlePlan
|
||||||
- bottle_cleanup_plan: DockerBottleCleanupPlan
|
- bottle_cleanup_plan: DockerBottleCleanupPlan
|
||||||
- bottle: _DockerBottle handle
|
- bottle: DockerBottle handle
|
||||||
- platform: DockerBottlePlatform
|
- 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
|
`from claude_bottle.bottles.docker import DockerBottlePlatform` keeps
|
||||||
working.
|
working.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .bottle import DockerBottle
|
||||||
from .bottle_cleanup_plan import DockerBottleCleanupPlan
|
from .bottle_cleanup_plan import DockerBottleCleanupPlan
|
||||||
from .bottle_plan import DockerBottlePlan
|
from .bottle_plan import DockerBottlePlan
|
||||||
from .platform import DockerBottlePlatform
|
from .platform import DockerBottlePlatform
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
"DockerBottle",
|
||||||
"DockerBottleCleanupPlan",
|
"DockerBottleCleanupPlan",
|
||||||
"DockerBottlePlan",
|
"DockerBottlePlan",
|
||||||
"DockerBottlePlatform",
|
"DockerBottlePlatform",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""_DockerBottle — concrete Bottle handle yielded by
|
"""DockerBottle — concrete Bottle handle yielded by
|
||||||
DockerBottlePlatform.launch.
|
DockerBottlePlatform.launch.
|
||||||
|
|
||||||
Holds the container name plus the in-container prompt path so
|
Holds the container name plus the in-container prompt path so
|
||||||
@@ -10,8 +10,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from .. import Bottle
|
||||||
|
|
||||||
class _DockerBottle:
|
|
||||||
|
class DockerBottle(Bottle):
|
||||||
"""Concrete Bottle for Docker."""
|
"""Concrete Bottle for Docker."""
|
||||||
|
|
||||||
def __init__(self, container: str, teardown, prompt_path_in_container: str | None):
|
def __init__(self, container: str, teardown, prompt_path_in_container: str | None):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
.prepare(spec, stage_dir=...) -> DockerBottlePlan
|
.prepare(spec, stage_dir=...) -> DockerBottlePlan
|
||||||
.launch(plan) -> ContextManager[_DockerBottle]
|
.launch(plan) -> ContextManager[DockerBottle]
|
||||||
.prepare_cleanup() -> DockerBottleCleanupPlan
|
.prepare_cleanup() -> DockerBottleCleanupPlan
|
||||||
.cleanup(plan) -> None
|
.cleanup(plan) -> None
|
||||||
.list_active() -> None
|
.list_active() -> None
|
||||||
@@ -25,7 +25,7 @@ from ...env_resolve import env_resolve
|
|||||||
from ...log import die, info
|
from ...log import die, info
|
||||||
from .. import BottleCleanupPlan, BottlePlan, BottlePlatform, BottleSpec
|
from .. import BottleCleanupPlan, BottlePlan, BottlePlatform, BottleSpec
|
||||||
from . import util as docker_mod
|
from . import util as docker_mod
|
||||||
from .bottle import _DockerBottle
|
from .bottle import DockerBottle
|
||||||
from .bottle_cleanup_plan import DockerBottleCleanupPlan
|
from .bottle_cleanup_plan import DockerBottleCleanupPlan
|
||||||
from .bottle_plan import DockerBottlePlan
|
from .bottle_plan import DockerBottlePlan
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ class DockerBottlePlatform(BottlePlatform):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@contextmanager
|
@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."""
|
"""Build, launch, and provision a Docker bottle. Teardown on exit."""
|
||||||
assert isinstance(plan, DockerBottlePlan), (
|
assert isinstance(plan, DockerBottlePlan), (
|
||||||
f"DockerBottlePlatform.launch expects DockerBottlePlan, "
|
f"DockerBottlePlatform.launch expects DockerBottlePlan, "
|
||||||
@@ -187,7 +187,7 @@ class DockerBottlePlatform(BottlePlatform):
|
|||||||
|
|
||||||
prompt_path = self._provision_container(plan, container)
|
prompt_path = self._provision_container(plan, container)
|
||||||
|
|
||||||
bottle = _DockerBottle(container, teardown, prompt_path)
|
bottle = DockerBottle(container, teardown, prompt_path)
|
||||||
yield bottle
|
yield bottle
|
||||||
finally:
|
finally:
|
||||||
teardown()
|
teardown()
|
||||||
|
|||||||
Reference in New Issue
Block a user