fix: add close/context-manager to diagnosis store; tighten import boundary
This commit is contained in:
@@ -10,9 +10,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from core.evolution.types import DiagnosisSignalRow
|
from core.evolution.types import DiagnosisSignalRow
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from types import TracebackType
|
||||||
|
|
||||||
# 表列顺序即 DiagnosisSignalRow 字段顺序(question_id..session_id),
|
# 表列顺序即 DiagnosisSignalRow 字段顺序(question_id..session_id),
|
||||||
# upsert 写入与 load 还原共用,避免手写列名两处漂移。
|
# upsert 写入与 load 还原共用,避免手写列名两处漂移。
|
||||||
_COLUMNS: tuple[str, ...] = (
|
_COLUMNS: tuple[str, ...] = (
|
||||||
@@ -152,3 +156,18 @@ class SqliteDiagnosisSignalStore:
|
|||||||
)
|
)
|
||||||
for r in cursor.fetchall()
|
for r in cursor.fetchall()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
"""关闭底层 SQLite 连接,释放文件描述符与锁。"""
|
||||||
|
self._conn.close()
|
||||||
|
|
||||||
|
def __enter__(self) -> SqliteDiagnosisSignalStore:
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(
|
||||||
|
self,
|
||||||
|
exc_type: type[BaseException] | None,
|
||||||
|
exc_val: BaseException | None,
|
||||||
|
exc_tb: TracebackType | None,
|
||||||
|
) -> None:
|
||||||
|
self.close()
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
"""core/evolution/ 子包的只读 Protocol 定义。
|
"""core/evolution/ 子包的持久化 Protocol 定义。
|
||||||
|
|
||||||
三个 Protocol 均为只读——core/ 返回结果 dataclass,写入由 app/ 持久化。
|
SkillStore / PromptStore / RunLog 为只读——core/ 返回结果 dataclass,
|
||||||
SkillStore / PromptStore 为同步(文件读取量小且快),RunLog 为异步
|
读取由 app/ 落盘的资源。SkillStore / PromptStore 同步(文件读取量小且快),
|
||||||
(隔离 SQLite 查询,core/ 不写 SQL)。
|
RunLog 异步(隔离 SQLite 查询,core/ 不写 SQL)。
|
||||||
|
DiagnosisSignalStore 兼具读写:逐题 upsert 诊断信号并支持断点续跑查询,
|
||||||
|
同样隔离 SQLite 实现,app/core 不写裸 SQL。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|||||||
@@ -4,7 +4,12 @@
|
|||||||
load 往返还原,以及 INFRA 行的可空字段处理。
|
load 往返还原,以及 INFRA 行的可空字段处理。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from adapters.baseline_diagnosis_store import DiagnosisSignalRow, SqliteDiagnosisSignalStore
|
import sqlite3
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from adapters.baseline_diagnosis_store import SqliteDiagnosisSignalStore
|
||||||
|
from core.evolution.types import DiagnosisSignalRow
|
||||||
|
|
||||||
|
|
||||||
def _store(tmp_path):
|
def _store(tmp_path):
|
||||||
@@ -78,3 +83,16 @@ def test_null_fields_for_infra_row(tmp_path):
|
|||||||
)
|
)
|
||||||
rows = s.load("r", "fp")
|
rows = s.load("r", "fp")
|
||||||
assert rows[0].error_type is None and rows[0].evolution_target is None and rows[0].infra is True
|
assert rows[0].error_type is None and rows[0].evolution_target is None and rows[0].infra is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_context_manager_closes_connection(tmp_path):
|
||||||
|
# with 块退出后连接关闭,再操作应报 ProgrammingError
|
||||||
|
with SqliteDiagnosisSignalStore(str(tmp_path / "h.db")) as s:
|
||||||
|
s.upsert(
|
||||||
|
DiagnosisSignalRow(
|
||||||
|
"q4", "v4", "r", "fp", "Counting", None, None, "T0", None, False, True, None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert s.done_question_ids("r", "fp") == {"q4"}
|
||||||
|
with pytest.raises(sqlite3.ProgrammingError):
|
||||||
|
s.done_question_ids("r", "fp")
|
||||||
|
|||||||
Reference in New Issue
Block a user