Files
bot-bottle/bot_bottle/db_store.py
T
didericis-claude 142974a4b8
test / unit (pull_request) Successful in 58s
test / integration (pull_request) Successful in 18s
test / coverage (pull_request) Successful in 1m6s
lint / lint (push) Successful in 1m55s
prd-number / assign-numbers (push) Successful in 16s
test / unit (push) Successful in 54s
test / integration (push) Successful in 19s
test / coverage (push) Successful in 1m6s
Update Quality Badges / update-badges (push) Successful in 1m2s
refactor: rename check_migrations → is_migrated
Boolean-returning methods should read as predicates.
Renames DbStore.check_migrations, StoreManager.check_migrations,
and the test patch accordingly.
2026-07-06 19:19:49 +00:00

60 lines
1.9 KiB
Python

"""Shared SQLite-backed store base class for bot-bottle (PRD 0013)."""
from __future__ import annotations
import sqlite3
from pathlib import Path
try:
from .migrations import TableMigrations
except ImportError:
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
class DbVersionError(Exception):
"""Raised when the on-disk schema is behind the current migration list."""
class DbStore:
"""Base for SQLite-backed stores. Subclasses resolve db_path then call super().__init__."""
def __init__(self, db_path: Path, migrations: TableMigrations) -> None:
self.db_path = db_path
self._migrations = migrations
self.db_path.parent.mkdir(parents=True, exist_ok=True)
def _connect(self) -> sqlite3.Connection:
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row
return conn
def is_migrated(self) -> bool:
"""Return True if the DB is fully up-to-date, False if migration is needed."""
if not self.db_path.exists():
return False
try:
with self._connect() as conn:
row = conn.execute(
"SELECT version FROM schema_versions WHERE module = ?",
(self._migrations.schema_key,),
).fetchone()
except sqlite3.OperationalError:
return False
version = row[0] if row else 0
return version == len(self._migrations.migrations)
def migrate(self) -> None:
"""Apply any pending migrations and set permissions on the DB file."""
with self._connect() as conn:
self._migrations.apply(conn)
self._chmod()
def _chmod(self) -> None:
try:
self.db_path.chmod(0o600)
except OSError:
pass
__all__ = ["DbStore", "DbVersionError"]