Skip to content

Fix/export bug#101

Merged
AfishInLake merged 20 commits into
masterfrom
fix/export_bug
Jul 12, 2026
Merged

Fix/export bug#101
AfishInLake merged 20 commits into
masterfrom
fix/export_bug

Conversation

@AfishInLake

Copy link
Copy Markdown
Owner

📌 关联 Issue

  • Closes #xxx
  • Related to #yyy

🎯 变更概述

  • 兼容现有功能(默认选项)
  • 包含破坏性更新(如勾选,请完整填写以下内容)

⚠️ 破坏性更新说明(仅当上方勾选时填写)

  • 受影响的用户场景

    (例如:使用 v0.2 格式配置文件的用户升级后将遇到解析错误)

  • 具体变更点

    (例如:YAML 配置中 style.font_size 字段已移除,改为嵌套结构 style.font.size

  • 迁移建议

    (例如:请将 font_size: 12 更新为 font: { size: 12 }

  • 是否提供自动迁移工具?

    • 是(路径:scripts/migrate_v02_to_v03.py

✅ 提交前自查清单

🔹 测试保障

  • 已通过 make test(或等效命令)
  • 新增/修改的功能有对应单元测试(位于 tests/ 目录)
  • 所有测试均使用 io.BytesIO,未保存任何临时 .docx 文件到磁盘
  • 测试覆盖了正常路径、边界情况和错误输入

🔹 配置与兼容性

  • YAML 配置变更向后兼容(旧配置仍能运行,或提供迁移说明)
  • 新增配置项已在 example/ 中提供示例
  • 不依赖未声明的第三方库(requirements.txtpyproject.toml 已更新)

🔹 文档与示例

  • README.md 已更新(如新增功能说明、命令参数)
  • 示例文档(example/)能正常运行且反映最新能力
  • 复杂逻辑已添加必要注释(尤其是涉及 python-docx 底层操作的部分)

🔹 安全与资源

  • 无敏感信息泄露(如 API Key、本地路径)
  • 无内存泄漏风险(如大文档处理时及时释放对象)
  • 未使用不安全的反序列化(如 pickleyaml.load 未指定 Loader

🔹 其他

  • 分支基于最新 master 切出,无无关提交
  • Commit 信息清晰(建议遵循 Conventional Commits,如 feat: add table caption check

💡 温馨提示:勾选 [x] 表示你已确认该项。未勾选项可能成为 Review 阻塞点。

- CONFIG_PATH 与 NODE_TYPE 相同时自动回退,删除 8 个冗余声明
- FormatNode.load_config 合并到 TreeNode.load_config(消除重复)
- 删除死代码 load_yaml_config 及无用 import
- 新增 @register 装饰器,替代手动维护 CATEGORY_TO_CLASS/LEVEL_MAP
- 修复 _write_comment_groups 中重复统计 bug
核心变更:
- 每个 FormatNode 子类声明 DEFAULTS 字典代替 Pydantic 模型
- load_config 合并 DEFAULTS + YAML → DotDict(保留 cfg.alignment 点号访问)
- 删除 CONFIG_MODEL、CONFIG_PATH、FormatNode 泛型参数
- 删除 BaseHeadingNode/BaseKeywordsNode 自定义 load_config
- config/loader.py 直接返回 dict(不再依赖 Pydantic 验证)
- NodeConfigRoot 改为 dict 包装器(向后兼容)
- 新增 BASE_FORMAT 共享默认值、@register 自动注册装饰器

剩余:68 个测试失败,主要是直接构造 Pydantic 模型的测试断言需更新
- TestLoadConfig → 测试实际配置值(不再检查 _pydantic_config)
- 删除测试已移除行为的测试(TypeError、ValueError)
- DotDict.__getattr__ 缺失 key 返回 None(更安全)
- 修复 node.py handler 检查方式(hasattr → is None)

41/953 失败,大部分是 KeywordsCoverageBoost 和 Caption 测试
- 938 passed, 3 failed(均为预先存在的 bug,与本次改动无关)
- 测试不再依赖 Pydantic 模型实例
- DotDict.__getattr__ 缺失 key 返回 None(安全默认值)
- NodeConfigRoot 支持 __getattr__ 点号访问(向后兼容)
- 修复 caption.py numbering_cfg 兼容 dict/DotDict
- 修复 pipeline numbering 字段缺失
- models.py 精简至 NodeConfigRoot + 编号兼容层(49行 vs 500行)
- 删除 TestDataModelValidation(测试已移除的 Pydantic 验证)
- 测试中所有 Pydantic 模型构造替换为 NodeConfigRoot/dict
- numbering.py isinstance 检查改为 dict 检查
- 932 passed, 2 failed(预存 bug)
- registry.py 新增 export_defaults(),遍历所有 @register 类
  按 NODE_TYPE 路径重建完整 YAML 配置树
- CLI `wordf config -o` 改用 export_defaults() 导出
- 消除各文件中重复的 _BASE/_KW/_H/_ACK/_REF_BASE,
  统一用 dotdict.BASE_FORMAT
- 修复测试 YAML 缺失字段(font_color, english_font_name 等)
- 934 passed, 0 failed
_show_config() 改用 export_defaults() + yaml.dump,
遍历所有注册类打印完整字段和默认值。
test_rules.py (1288行) → 8 个文件:
- test_node.py (298行): FormatNode 基类测试
- test_abstract.py (443行): 摘要相关节点测试
- test_heading.py (265行): 标题节点测试
- test_keywords.py (178行): 关键词节点测试
- test_body.py (194行): 正文节点测试
- test_caption.py (152行): 题注节点测试
- test_references.py (178行): 参考文献节点测试
- test_acknowledgement.py (202行): 致谢节点测试

934 passed, 0 failed
- rules/ 子目录: test_abstract, test_acknowledgement, test_body,
  test_caption, test_heading, test_keywords, test_node, test_references
- style/ 子目录: test_defs, test_diff, test_reader, test_writer
- api/, classify/ 子目录
- test_integration.py, test_coverage_boost.py 待后续拆分
- 943 passed, 0 failed
- rules/ 子目录: 8 个文件对应 rules/*.py
- style/ 子目录: 4 个文件对应 style/*.py
- api/, classify/, utils/ 子目录
- 新增 tests/utils/test_text.py(罗马数字、中文数字、题注解析)
- 976 passed, 0 failed
- test_coverage_boost, test_caption_numbering, test_integration 暂保持独立
  待后续拆入 pipeline/, agent/, config/, structure/ 子目录
已迁移到子目录:
- rules/: test_abstract, test_acknowledgement, test_body, test_caption,
  test_heading, test_keywords, test_node, test_references
- style/: test_defs, test_diff, test_reader, test_writer
- api/: test_api
- classify/: test_tag

保持在根目录(自包含、待后续清理):
- test_integration.py, test_coverage_boost.py
- test_caption_numbering.py, test_numbering.py
- test_tree.py, test_utils.py

943 passed, 0 failed
- config/loader.py: LazyConfig 改为普通类(移除 __new__ 单例),
  每个实例独立加载;测试可创建独立实例避免全局状态污染
- style/diff.py: 模块变量 style_checks_warning 改为 _warnings_cache
  懒加载,_load_warnings() 处理配置未加载的情况
- conftest.py: 更新 reset_style_warning fixture
- 943 passed, 0 failed
…ption

- tests/utils/test_text.py (186行): FromRoman, FromChineseNum, ParseCaptionText
  零外部依赖,只 import wordformat.utils 的三个函数
- tests/rules/test_caption.py (778行): 合并题注格式测试 + 编号集成测试
- 删除 test_caption_numbering.py
- 943 passed, 0 failed
- test_tree.py: +TestDotDict (5测) +TestExportDefaults (1测)
- rules/test_caption.py: +TestCaptionEdgeCases (3测) +TestNodeConfigRoot (3测)
- 覆盖 dotdict.__delattr__, deep_merge, export_defaults,
  NodeConfigRoot.__setattr__/__getattr__, caption 边界分支
- CLAUDE.md: 更新架构描述(移除 set_style/set_tag/datamodel/word_structure 等过期引用),
  测试文件路径(test_rules.py → rules/*.py),覆盖阈值(85→87%),
  FormatNode 子类模式(移除 CONFIG_MODEL/CONFIG_PATH,添加 DEFAULTS/@register)
- docs/usage.md: 修正 Python API import 路径
  (wordformat.set_tag → wordformat.classify.tag,
   wordformat.set_style → wordformat.pipeline.orchestrate)
- docs/configuration.md: 修正配置验证说明(移除 Pydantic 字段校验,
  改为 YAML 语法校验 + wordf config -o 导出)
- _color_last_comment: 修复 Comments 对象不支持下标的问题,
  改用 _comments_elm XML 树找最后一条批注
- _write_comment_groups: 严重→红(FF0000),提醒→蓝(0000FF),一般→默认黑
- FigureImage.DEFAULTS: 补 alignment + first_line_indent 默认值,
  兜底 None → "居中对齐"/"0字符"
- 严重度合并为两档:错误(红 FF0000) / 提醒(蓝 0000FF)
- 新增 add_styled_comment/apply_run_style/split_comment_line 接口,可单独设置批注片段样式
- 移除事后改 XML 的 _color_last_comment,改用 python-docx Comment run API
新增 style/inheritance.py:按 OOXML 五层链(直接格式→字符样式→段落样式
basedOn 链→docDefaults→主题字体)解析有效格式,提取器注册表可扩展。
所有 reader getter 委托 resolver,删除各处手写 base_style 循环与
_get_style_spacing / _get_with_style_fallback。

修复 run 级 bold/italic/underline/字体/字号此前只读直接格式、漏样式继承
导致的标题类误报。行为变化:空 run 字号 12.0→11.0(docDefaults)、
英文字体 None→主题字体。

新增 tests/style/test_inheritance.py(33 例);删除针对已移除内部函数的
旧用例,行为变化用例改用真实 basedOn 链。959 passed,覆盖率 87.67%。
- inheritance.py: run 提取器改用 CT_RPr 类型化子元素(.b/.i/.u/.sz/.color/
  .style),主题命名空间用 qn("a:*"),parse_xml 替代裸 lxml,
  Mock 守卫改用 BaseOxmlElement
- reader.py: 元素守卫改用 BaseOxmlElement
- hyperlinks.py: 超链接属性用 CT_Hyperlink 的 .anchor/.history 描述符
- body.py: w:vertAlign 手工增删改用 Font.superscript / CT_RPr.superscript

东亚/主题字体(CT_Fonts 未建模)、CJK 单位(行/字符)、szCs/bCs 伴随元素、
自动编号生成等 python-docx 覆盖不到的部分保持裸 XML。959 passed。
- config/loader: LazyConfig 类简化为 load_config + get_config 两个函数
- diff.py: _WARNING_DEFAULTS 改为 WarningConfig dataclass,registry.py 从此导出
- diff.py: _char/_para_warning_enabled 合并为 _warning_enabled,warnings 注入 to_string
- stages.py: _fix_style_* 优先使用 Style.font / Style.paragraph_format API
- writer.py: SetSpacing.set_hang() 用 get_or_add_pPr() 替代 parse_xml
- comments.py: para.clear() 替代 XML 元素逐个删除
- units.py: convert_to_emu 用 docx.shared 替代手写 EMU 换算
- defs.py: UnitLabelEnum 通过 _meta_funcs 非空自动判断是否 split_unit;FontSize 覆盖 rel_value
- defs.py: LineSpacingRule / Alignment 加 _LABEL_MAP_REVERSE,diff.py 不再硬编码标签映射
- inheritance.py: 全部函数补 docstring
- reader.py: _para/_run/_run_font 内部函数补 docstring
@AfishInLake
AfishInLake merged commit c82f65f into master Jul 12, 2026
12 checks passed
@AfishInLake
AfishInLake deleted the fix/export_bug branch July 12, 2026 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant