refactor: extract supervise types to _supervise_types, eliminate get_supervise_mod()

Proposal, Response, AuditEntry, host_db_path, bot_bottle_root, and their
shared constants/helpers are moved to a new _supervise_types.py module.
queue_store and audit_store now import these directly instead of deferring
through get_supervise_mod() at call time.

host_db_path() in _supervise_types uses sys.modules[__name__].bot_bottle_root()
so monkey-patches on _supervise_types.bot_bottle_root propagate through it.
supervise.host_db_path() is re-defined to call bot_bottle_root() through
supervise's own globals, preserving patchability for callers that go through
the supervise module.

Test fixtures that create stores without an explicit db_path now patch
_sv_types.bot_bottle_root alongside supervise.bot_bottle_root. The server
test patches both flat and package _supervise_types since handle_tools_call
runs in the package context while the responder thread uses the flat module.

Dockerfile.sidecars gets a COPY for _supervise_types.py so the sidecar
bundle retains its flat import chain.

Follow-up to issuecomment-2872 on PR #320.
This commit is contained in:
2026-07-06 17:47:52 +00:00
parent ed8fbfdfa8
commit e7e8c7fdb4
8 changed files with 288 additions and 217 deletions
+28 -6
View File
@@ -8,6 +8,7 @@ from datetime import datetime, timezone
from pathlib import Path
from bot_bottle import supervise
from bot_bottle import _supervise_types as _sv_types
from bot_bottle.audit_store import AuditStore
from bot_bottle.queue_store import QueueStore
from bot_bottle.supervise import (
@@ -122,13 +123,20 @@ class TestQueueIO(unittest.TestCase):
self._tmp.cleanup()
def _patch_home(self, fake_home: Path):
original = supervise.bot_bottle_root
original_sv = supervise.bot_bottle_root
original_svt = _sv_types.bot_bottle_root
def fake_root() -> Path:
return fake_home / ".bot-bottle"
supervise.bot_bottle_root = fake_root # type: ignore[assignment]
return lambda: setattr(supervise, "bot_bottle_root", original)
_sv_types.bot_bottle_root = fake_root # type: ignore[assignment]
def restore() -> None:
supervise.bot_bottle_root = original_sv # type: ignore[assignment]
_sv_types.bot_bottle_root = original_svt # type: ignore[assignment]
return restore
def test_write_and_read_proposal(self):
p = _proposal()
@@ -232,13 +240,20 @@ class TestAuditLog(unittest.TestCase):
self._tmp.cleanup()
def _patch_home(self, fake_home: Path):
original = supervise.bot_bottle_root
original_sv = supervise.bot_bottle_root
original_svt = _sv_types.bot_bottle_root
def fake_root() -> Path:
return fake_home / ".bot-bottle"
supervise.bot_bottle_root = fake_root # type: ignore[assignment]
return lambda: setattr(supervise, "bot_bottle_root", original)
_sv_types.bot_bottle_root = fake_root # type: ignore[assignment]
def restore() -> None:
supervise.bot_bottle_root = original_sv # type: ignore[assignment]
_sv_types.bot_bottle_root = original_svt # type: ignore[assignment]
return restore
def test_write_then_read_single_entry(self):
e = AuditEntry(
@@ -385,13 +400,20 @@ class TestSupervisePrepare(unittest.TestCase):
self._tmp.cleanup()
def _patch_home(self, fake_home: Path):
original = supervise.bot_bottle_root
original_sv = supervise.bot_bottle_root
original_svt = _sv_types.bot_bottle_root
def fake_root() -> Path:
return fake_home / ".bot-bottle"
supervise.bot_bottle_root = fake_root # type: ignore[assignment]
return lambda: setattr(supervise, "bot_bottle_root", original)
_sv_types.bot_bottle_root = fake_root # type: ignore[assignment]
def restore() -> None:
supervise.bot_bottle_root = original_sv # type: ignore[assignment]
_sv_types.bot_bottle_root = original_svt # type: ignore[assignment]
return restore
def test_prepare_creates_queue(self):
plan = _StubSupervise().prepare("dev", self.stage_dir)
+10 -2
View File
@@ -12,6 +12,7 @@ from pathlib import Path
from unittest.mock import patch
from bot_bottle import supervise
from bot_bottle import _supervise_types as _sv_types
from bot_bottle.audit_store import AuditStore
from bot_bottle.cli import supervise as supervise_cli
from bot_bottle.queue_store import QueueStore
@@ -54,13 +55,20 @@ class _FakeHomeMixin:
def _setup_fake_home(self):
self._tmp = tempfile.TemporaryDirectory(prefix="supervise-test.")
original = supervise.bot_bottle_root
original_sv = supervise.bot_bottle_root
original_svt = _sv_types.bot_bottle_root
def fake_root() -> Path:
return Path(self._tmp.name) / ".bot-bottle"
supervise.bot_bottle_root = fake_root # type: ignore[assignment]
self._restore_home = lambda: setattr(supervise, "bot_bottle_root", original)
_sv_types.bot_bottle_root = fake_root # type: ignore[assignment]
def restore() -> None:
supervise.bot_bottle_root = original_sv # type: ignore[assignment]
_sv_types.bot_bottle_root = original_svt # type: ignore[assignment]
self._restore_home = restore
QueueStore("").migrate()
AuditStore().migrate()
+14 -2
View File
@@ -19,8 +19,10 @@ sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "bot_bott
import supervise as _sv # noqa: E402 # type: ignore
import queue_store as _qs # noqa: E402 # type: ignore
import audit_store as _as # noqa: E402 # type: ignore
import _supervise_types as _svt_flat # noqa: E402 # type: ignore
from bot_bottle import supervise_server # noqa: E402
from bot_bottle import _supervise_types as _svt_pkg # noqa: E402
from bot_bottle.supervise_server import (
ERR_INTERNAL,
ERR_INVALID_PARAMS,
@@ -277,13 +279,23 @@ class TestHandleToolsCall(unittest.TestCase):
self._tmp.cleanup()
def _patch_home(self, fake_home: Path):
original = _sv.bot_bottle_root
original_sv = _sv.bot_bottle_root
original_flat = _svt_flat.bot_bottle_root
original_pkg = _svt_pkg.bot_bottle_root
def fake_root() -> Path:
return fake_home / ".bot-bottle"
_sv.bot_bottle_root = fake_root # type: ignore[assignment]
return lambda: setattr(_sv, "bot_bottle_root", original)
_svt_flat.bot_bottle_root = fake_root # type: ignore[assignment]
_svt_pkg.bot_bottle_root = fake_root # type: ignore[assignment]
def restore() -> None:
_sv.bot_bottle_root = original_sv # type: ignore[assignment]
_svt_flat.bot_bottle_root = original_flat # type: ignore[assignment]
_svt_pkg.bot_bottle_root = original_pkg # type: ignore[assignment]
return restore
def _respond_when_proposal_appears(self, status: str, notes: str = "") -> threading.Thread:
"""Background thread: poll the queue for a fresh proposal, write a