Files
AirPlan-V2/lib/air_runtime/io.py
AirPlan 6130478c96 feat: AirPlan V2 — 全专家插件强制路由 + 事件系统规范化
P0-8 扩大: do_mode.py finish_worker 全专家插件强制路由
- GUI→XDB, network→NDB, C/C++→SDB, done→Rvr, blocked/failed→Dbg
- 证据去重: 已有 xdbSessions/ndbSessions/sdbReports/rvrReviewed 则跳过

P1-GAP17: 事件 emit 规范化
- 新增 7 个事件常量 (TASK_ENTERED, TASK_FINISHED, ENGINE_ENTERED 等)
- 全部 emit 调用替换字符串字面量为常量,零残留
- 30 个事件类型常量全部定义且唯一

P1-GAP18: 事件日志原子轮转
- emit 计数器每 128 次检查轮转,避免每次 emit 读文件
- 清除未使用的 _emit_with_completion/_pending_merge_complete
- 原子轮转: tempfile+os.replace 保证不损坏

eng 极端接管: 强制调用全部专家插件 (Dbg/XDB/NDB/SDB/Rvr)
commands/do.md: 更新为全专家插件路由文档

全量测试: 69 通过, 0 失败

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:56:44 +08:00

54 lines
1.7 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
原子 I/O 模块 — 消除 V1 5 份 _json_dump/_json_load 重复。
每次写入使用 tempfile + os.replace() 保证原子性,写入前自动备份。
"""
from __future__ import annotations
import contextlib
import json
import logging
import os
import tempfile
from pathlib import Path
logger = logging.getLogger(__name__)
def atomic_json_write(path: Path, data: dict | list, indent: int = 2) -> None:
"""POSIX 原子写入tempfile + os.replace()。写入前自动备份旧文件为 .bak单级轮转"""
path.parent.mkdir(parents=True, exist_ok=True)
# 备份旧文件
if path.exists():
bak = path.with_suffix(path.suffix + ".bak")
try:
os.replace(str(path), str(bak))
except OSError as exc:
logger.warning("backup %s -> %s failed: %s", path, bak, exc)
fd, tmp = tempfile.mkstemp(dir=path.parent, suffix=".tmp")
try:
os.write(fd, json.dumps(data, indent=indent, ensure_ascii=False).encode("utf-8"))
os.close(fd)
os.replace(tmp, path)
except BaseException:
with contextlib.suppress(OSError):
os.unlink(tmp)
raise
def safe_json_load(path: Path) -> dict | list | None:
"""安全加载:处理损坏文件,自动从 .bak 恢复。文件不存在返回 None。"""
try:
return json.loads(path.read_text("utf-8"))
except FileNotFoundError:
return None
except (json.JSONDecodeError, UnicodeDecodeError):
bak = path.with_suffix(path.suffix + ".bak")
if bak.exists():
logger.warning("corrupt %s, restoring from %s", path, bak)
return json.loads(bak.read_text("utf-8"))
logger.error("corrupt %s with no backup", path)
return None