拆 __init__.py 为 entry_* + 抽 StoragePort/runtime_state + 结构契约(结构批) #110
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "refactor/97-split-entry-modules"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #97
零新目录。2493 行的
__init__.py切成 7 个entry_*入口簇 + 5 个跨簇共享模块,包根只剩 48 行:注册触发 + host 公开面。
四个任务
__init__.py:前置抽runtime_state.py(_pending_flushes/_last_group_chat_activity/_silence_window/_debouncer四个共享状态);共享层另有
cost_metering/gate_pipeline/loop_factory/environment_pipeline。不做私有名 re-export,改了约 30 处测试 import,让遗漏以
ImportError立刻失败。StoragePort抽进storage_port.py,解掉ports.py(host 契约文件)→ 651 行内存实现这条全图唯一的"向上"边。
SentLog本体留在src/。visible_host_tools移进tool_port.py。形状与 AC 不同,见下。configure/get_ports/ArisePorts,ports.py那条一直写错调用路径的错误信息随之成立;ADR-0001 追加更新节(docs 分支本地提交)。
「只搬运」的机械证明
不靠"我看过了":AST 逐字比对拆分前后的全部顶层定义——85 个,0 丢失,仅 6 处差异,
且每处都核过:
_debouncer/_silence_window从裸名改成runtime_state.X模块属性访问。这不是洁癖:
from x import y在 import 时就把名字定死,而test_silence_window_wiring.py正是靠monkeypatch.setattr(..., "_silence_window", …)重绑它们——照搬
from ... import会让那两条 fixture 静默失效。_process_environment_signal的bot: Bot→bot: Any(见下)。任务三的形状被实测推翻了
AC 写的是「import-linter forbidden 契约禁
nonebot.adapters」。实测不可行:forbidden_modules一旦指向外部包的非顶层子模块(nonebot.adapters),import-linter在建图之前就显式拒绝,报
Invalid forbidden module nonebot.adapters: subpackages of external packages are not valid.(配置期ValueError,退出码 1,CI 会红)。改成两件工具各守一半(已与维护者确认):
tests/test_import_contract.py用 AST 精确守「非entry_*不得 importnonebot.adapters」。例外是一个 glob 而不是一张表,新增入口不需登记。它当场抓到了一处真违规(
environment_pipeline.py拆分过程中自己带上的from nonebot.adapters import Bot)——修法是把bot: Bot改成bot: Any,make_egress的契约本来就是Callable[[Any], EgressPort],那个标注是装饰性的。这是它真能表达的,也正是
runtime_state.py存在的理由。配置放
pyproject.toml而非.importlinter:后者按系统默认编码读,Windows(GBK)会直接读崩在中文注释上。
一处加强而非削弱的守卫
issue #88 那个
vars(plugin)机械枚举(命令边界覆盖面的唯一来源)会因为拆分而一个都枚举不到。没有收窄成
vars(entry_admin)——那样以后在别的entry_*里注册命令就看不见了,而它存在的全部理由就是"不靠手写清单"。改成扫全包。
变异验证:收窄回单模块 → 8 failed + 3 skipped。那 3 个 skip 正是「机械枚举驱动的覆盖
会静默消失」这个已知形状,靠等值守卫兜住了。
两轴 code review(sonnet):3 条 finding 存活,均已处理
我最初在 pyproject.toml/测试 docstring/CI 注释/ADR 更新节里写的"import-linter 契约会
静默通过"是错的——已独立复现(见上一节的准确描述),并按正文推理为准,改成
准确描述。属于本仓记过的「给不做的分支配一个听起来合理但没核对到位的理由」这一类,
这是第九次。结论不变(import-linter 确实表达不了那条规则),理由订正。
runtime_state.py把只被反应式一条入口碰的_debouncer写成了「被三条入口碰」;
gate_pipeline.py漏了即刻追问也调_evaluate_unified_gate(实际四条触发路径,不是三条)。逐条 grep 复核后改正。
loop_factory.py那条(模块级摘要写"四条入口"、函数级 docstring 写"五条路径")经核实是两个不同的合法粒度,不是缺陷,未改。
conftest.py加载顺序约束那条降级为「锦上添花」,未改。验证
-n auto各一遍),ruff check干净,lint-imports2 kept / 0 broken。import 入口层 / 穷举守卫收窄回单模块。还原后 md5 全部一致。
仍值得独立复核的两点
_debouncer/_silence_window是被重绑的,改成了属性访问;其余(
_pending_flushes/_last_group_chat_activity/_pending_followup_checks/_pending_sticker_learning/_pending_cross_platform_pulls/_in_flight_pull_users/_delegate_task_registry)判断都是原地修改,用from ... import安全。判错一个就是一条静默失效。_process_environment_signal该不该独立成模块"这类是判断题,值得复看。
🤖 Generated with Claude Code