# `macro_split` 重构为可安装包 `macro_lactone_toolkit` 的实施计划 ## Summary 采用“渐进式重构,不推倒重写”的方案:保留仓库里已经可用的 12-20 元环识别、通用编号、侧链 BFS 提取和 dummy 原子思路,删除或降级 16 元环专用路径,把核心能力统一收敛到一个正式可安装、可测试、可 CLI 调用的包。 本次重构完成后,项目要满足这几个结果: - 可以作为正式 Python 包安装和导入,主包名为 `macro_lactone_toolkit` - 默认自动识别 12-20 元有效大环内酯;也允许显式传 `ring_size` - 支持环编号和侧链裂解 - 裂解结果同时提供“带位置编号 dummy”与“普通 `*` dummy”两套 SMILES,且都能被 RDKit 读取保存 - 用 `pixi` 管理环境和测试 - 先执行 `git pull --ff-only` 同步远端,再开始任何代码改动 ## Key Changes ### 1. 仓库与包结构正规化 - 第一动作不是改代码,而是在仓库根目录执行: - `git status --short` - `git pull --ff-only` - 将当前直接放在顶层 `src/` 下的运行时代码,改成标准包布局:`src/macro_lactone_toolkit/` - 不保留 `src.*` 兼容层;README、测试、脚本、入口点全部切到新导入路径 - `pyproject.toml` 改为标准 setuptools `src` layout 配置,修正: - 包发现 - 项目名与版本元数据 - console scripts - pytest 配置 - `pixi.toml` 统一为 Python 3.12,并支持至少: - `osx-arm64` - `linux-64` - `pixi.toml` 补齐测试依赖和常用任务: - `test` - `lint`(只做检查,不自动改写文件) - `smoke-import` - README 中所有 `from src...` 示例全部替换为 `from macro_lactone_toolkit...` ### 2. 统一核心领域模型与 API 以 `ring_visualization.py` 的通用逻辑和参考脚本的编号思路为基础,重构出清晰边界的核心模块,避免“16 元环专用”和“12-20 元环通用”并存。 公开 API 统一为: - `macro_lactone_toolkit.MacroLactoneAnalyzer` - `macro_lactone_toolkit.MacrolactoneFragmenter` - `macro_lactone_toolkit.RingNumberingResult` - `macro_lactone_toolkit.SideChainFragment` - `macro_lactone_toolkit.FragmentationResult` 行为约定固定如下: - `MacroLactoneAnalyzer` - 负责识别 12-20 元环 - 验证环上是否存在有效内酯酯键 - 返回所有命中的有效环尺寸 - `MacrolactoneFragmenter` - 默认自动识别有效环尺寸 - 若检测到 0 个有效环,抛出明确异常 - 若检测到多个有效环尺寸,默认抛出“歧义异常”,要求调用者显式传 `ring_size` - 若调用者传了 `ring_size`,则只处理该环 - `RingNumberingResult` - 至少包含:`ring_size`、`ring_atoms`、`ordered_atoms`、`carbonyl_carbon_idx`、`ester_oxygen_idx`、`atom_to_position`、`position_to_atom` - `SideChainFragment` - 至少包含:`parent_id`、`cleavage_position`、`attachment_atom_idx`、`fragment_smiles_labeled`、`fragment_smiles_plain`、`atom_count`、`molecular_weight` - `FragmentationResult` - 至少包含:母分子信息、选定环尺寸、编号结果、所有碎片、错误/警告信息 异常策略固定为: - `MacrolactoneError` - `MacrolactoneDetectionError` - `AmbiguousMacrolactoneError` - `RingNumberingError` - `FragmentationError` ### 3. 编号与裂解算法收敛 编号逻辑以“通用 12-20 元环”作为唯一正式实现,不再让旧的 16 元环 `ring_numbering.py` 充当主入口。 - 编号规则固定: - 位置 1 = 环上的内酯羰基碳 - 位置 2 = 环上的酯键氧 - 位置 3-N = 沿确定方向依次编号剩余环原子 - 编号实现使用统一入口,不能再出现批处理脚本内部偷偷调用 `[r16]` 专用函数的情况 - 自动识别时,先找所有 12-20 元环,再验证该环是否为有效大环内酯 - 侧链识别统一用“环原子邻居中不在环内的原子即侧链起点” - 侧链提取统一用 BFS,只沿非环原子扩展 - dummy 原子输出固定两套: - `fragment_smiles_labeled`:带位置编号 dummy,例如 `[5*]` - `fragment_smiles_plain`:普通 `*` - dummy 与连接原子的键型必须保留原始键型,不能强制都变成单键 - 所有裂解结果必须通过 RDKit round-trip: - `Chem.MolFromSmiles(...)` 成功 - `Chem.MolToSmiles(...)` 可再次序列化 ### 4. CLI、批处理与现有脚本归并 交付范围包含正式 CLI,不再依赖散乱脚本作为主要入口。 CLI 设计固定为 3 个命令: - `macro-lactone-toolkit analyze` - 输入单个 SMILES 或 CSV - 输出有效环尺寸、是否歧义、选中的环尺寸 - `macro-lactone-toolkit number` - 输入单个 SMILES - 输出编号结果 JSON - `macro-lactone-toolkit fragment` - 输入单个 SMILES 或 CSV - 输出碎片 JSON/CSV,包含 labeled/plain 两套 SMILES 批处理行为固定为: - CSV 默认读取 `smiles` 列 - 可选 `id` 列;未提供则自动生成 - 自动识别歧义分子时: - 默认跳过并记录错误 - 显式传 `--ring-size` 时按指定环处理 - 旧 `scripts/` 中保留价值的逻辑迁入正式 CLI;剩余脚本改成薄封装或标记为 legacy,不再承担核心实现 ## Test Plan 必须用 `pixi` 跑完整验证,至少覆盖下面这些场景: - 包安装与导入 - `pixi run python -c "import macro_lactone_toolkit"` - `pixi run pytest` - 环识别 - 单一有效环尺寸的 12、14、16、20 元环样例 - 无效分子 - 无内酯键分子 - 多有效环尺寸分子,确认抛出 `AmbiguousMacrolactoneError` - 编号 - 位置 1 必须是羰基碳 - 位置 2 必须是酯键氧 - 编号范围必须连续覆盖 `1..N` - 同一分子多次运行结果一致 - 裂解 - 无侧链位置返回空列表 - 有侧链位置返回碎片 - `fragment_smiles_labeled` 与 `fragment_smiles_plain` 都能被 RDKit 成功读取 - labeled dummy 保留位置号 - dummy 键型与原键型一致 - CLI - `analyze`、`number`、`fragment` 的基本 smoke test - CSV 批处理 - 歧义分子跳过与错误日志行为 - 回归 - 现有 splicing engine 若继续保留,至少保留一个 dummy 拼接回组装测试,确保 labeled dummy 方案可持续扩展 ## Assumptions - `git pull` 使用 `--ff-only`,避免在重构开始前引入不透明 merge commit - 本次不保留 `src.*` 导入兼容层,全面切换到 `macro_lactone_toolkit` - 本次把“正式库 API + 正式 CLI + pixi 测试”作为主交付;notebooks 只做参考,不作为正式接口 - 发现多个有效大环尺寸时,默认视为歧义并失败,不自动猜测 - 继续使用 RDKit 作为唯一结构解析与序列化基础