首页
/ Docling 开源贡献实战指南:uv 开发环境、代码风格、参考测试数据与文档工作流

Docling 开源贡献实战指南:uv 开发环境、代码风格、参考测试数据与文档工作流

2026-09-06 12:09:46作者:冯爽妲Honey

本文基于 Docling 仓库的 CONTRIBUTING.md 展开,系统梳理向 Docling 提交代码的完整工作流:如何用 uv 搭建可复现的开发环境、Ruff 与 ty 双工具链下的代码风格约束、DOCLING_GEN_TEST_DATA 参考测试数据的再生成机制,以及本地文档服务与部署方式。读完本文,你可以独立完成"克隆 → 建环境 → 改代码 → 跑检查 → 跑测试 → 更新文档"的全部贡献闭环,并理解每条命令在仓库配置文件中的真实落点。

一、开发环境:以 uv 为唯一的项目与包管理器

Docling 官方明确采用 uv 作为包与项目管理器(package and project manager)。CONTRIBUTING.md 给出的标准操作如下:

1.1 创建并同步项目环境

uv sync

uv sync 会在项目虚拟环境不存在时自动创建,并将项目依赖与 uv.lock 中的锁定版本同步到环境中。

1.2 指定 Python 版本(可选)

如果需要固定 Python 版本工作,先为该版本创建独立虚拟环境再同步:

uv venv --python 3.12
uv sync

适用前提:项目声明 requires-python = '>=3.10,<4.0'(见 pyproject.toml),即支持 Python 3.10 至 3.14,uv venv --python 3.12 落在支持区间内。

1.3 新增依赖

uv add [OPTIONS] <PACKAGES|--requirements <REQUIREMENTS>>

执行后 pyproject.tomluv.lock 会一并更新。CONTRIBUTING.md 强调不要手改 uv.lock——该文件在 CI 中通过 uv lock --locked 做只读校验(见 Makefilecheck-all 目标)。

1.4 仓库侧印证:开发依赖组与 workspace

pyproject.toml[dependency-groups] 可以看到仓库如何组织开发依赖:

  • typecheck 组:ty、各 types-*/*-stubs 类型存根;
  • dev 组:包含 prek>=0.3.10tachpytest~=9.0.3pytest-xdistnbqadprint-py 等,正是本文后续各检查工具的安装来源;
  • docs 组与 examples 组:分别用于文档构建与示例运行,默认不参与贡献者环境安装。

同时 pyproject.toml 声明了 uv workspace:

[tool.uv.workspace]
members = ["packages/docling", "packages/docling-client"]

[tool.uv]
package = true
default-groups = "all"

即根项目与 packages/doclingpackages/docling-client 两个成员包在同一 workspace 内解析,docling 依赖走 workspace = true 的本地源(pyproject.toml)。

对于希望"与 CI 完全一致"地建环境的贡献者,仓库提供了 Make 目标(见 Makefile):

make setup
# 等价于:uv sync --frozen --group dev --all-extras --no-group docs --no-group examples

--frozen 表示严格使用锁文件、不重新解析,--all-extras 安装全部可选格式/模型依赖,并显式排除 docsexamples 两个组——这就是 CONTRIBUTING.md 中 uv sync 流程在 CI 视角下的完整形态。

二、代码风格:Ruff + ty 双工具链,由 prek 统一驱动

CONTRIBUTING.md 的 "Coding Style Guidelines" 指明仓库用两类工具强制执行风格:

  • Ruff:同时承担 linter 与代码格式化器;
  • ty:静态类型检查器。

而所有风格检查与回归测试钩子由 prek(pre-commit 兼容配置的快速 runner)统一定义和管理。

2.1 安装与运行检查

让钩子在每次提交前自动执行:

uv run prek install

对全部文件按需手动触发:

uv run prek run --all-files

一个容易踩的坑,CONTRIBUTING.md 专门给出提示:Ruff 这类"会改写文件"的钩子在修改了文件后会报告失败(hook runner 不接受钩子改动了工作区)。此时把被修改的文件 git add 后再次 git commit 即可通过。

2.2 钩子的真实清单(.pre-commit-config.yaml)

.pre-commit-config.yaml 定义了 prek 实际执行的钩子链(fail_fast: true,任一失败即中止):

钩子 作用 关键参数
trailing-whitespace / end-of-file-fixer 清理行尾空白、保证文件以换行结尾 限定 doclingtestsperfsdocs/examples.py 及根级配置文件
check-yaml / check-toml / check-merge-conflict YAML/TOML 合法性与冲突标记检查 --unsafe
check-added-large-files 拦截超大文件入库 --maxkb=5120(5 MB)
insert-license 自动插入 SPDX 许可证头 模板为 .github/license-header.txt,检测前 5 行已有许可声明
ruff(linter) 静态检查并自动修复 --exit-non-zero-on-fix --fix --config=pyproject.toml,覆盖 .py.ipynb
ruff-format 格式化 --config=pyproject.toml
ty(local 钩子) 静态类型检查 实际执行 uv run --no-sync ty check,作用于 docling/.github/scripts
tach(local 钩子) 模块依赖分层检查 uv run --no-sync tach check
tach-module-coverage 校验 tach 模块清单覆盖率 scripts/check_tach_module_coverage.py
max-lines 单文件行数上限 --max-lines=1500,实现见 scripts/check_max_lines.py
dprint 非 Python 文件(TOML 等)格式检查 配置 .github/dprint.json
uv-lock 提交前校验锁文件一致性

注意 ruff 钩子中的 --exit-non-zero-on-fix:只要 Ruff 自动修复了任何内容就返回非零——这正是 2.1 节"先 git add 再提交一次"提示的机制来源。

2.3 Ruff 与 ty 的具体配置

Ruff 的完整规则在 pyproject.toml

  • target-version = "py310"line-length = 88
  • 启用的规则族:C(comprehensions)、C9(复杂度)、E/W(pycodestyle)、F(pyflakes)、I(isort)、PD(pandas-vet)、PIEQRUFS307(禁 eval)、ASYNCUP(pyupgrade);
  • 复杂度上限 max-complexity = 30
  • per-file-ignores__init__.py 豁免 E402/F401,测试目录豁免 ASYNC

ty 的配置在 pyproject.toml[tool.ty.rules] 将所有规则级别设为 warn[tool.ty.environment] 按 Python 3.10 做语义解析;[tool.ty.src] 的检查范围限定为 docling.github/scripts;对 torchvisiontransformersdocling_parse 等约 20 个第三方包声明 allowed-unresolved-imports,即这些依赖不要求本地存在完整类型定义。

除 Ruff/ty 外,仓库还用 Tach 维护模块依赖的层级边界:tach.toml 将包划分为 entrypoints → clients → pipeline → models → core → foundation 六层(自上而下依赖),例如 docling.document_converter 属于 entrypoints 层(tach.toml)。从 tach.toml 的注释看,core 层内仍存在 datamodel 与 backend 的循环依赖簇,被标注为"代码级重构才能解开"——贡献者在新增跨模块导入时需要格外留意这一约束,make tachMakefile)可随时验证。

AGENTS.md 还补充了几条面向实现者的代码标准,可视为风格约束的延伸:跨模块/可序列化的稳定数据结构优先用 Pydantic 模型或 dataclass,避免松散字典;路径处理优先 pathlib.Path 而非 os.path;避免宽泛的 hasattr/getattr 探测模式;测试必须验证有意义的行为而非自我证明。

2.4 Makefile 中的等价检查命令

Makefile 把检查流程组织成四个常用目标(与 AGENTS.md 的 "Key commands" 一致):

make check     # 只读检查:ruff format --check + ruff check + ty check + tach check
               #             + 模块覆盖率/最大行数脚本 + dprint check + uv lock --locked
make validate  # 仅对当前改动集(git diff + 未跟踪文件)跑 prek run --files
make fix       # 可写修复:ruff format + ruff check --fix + dprint fmt
make test      # uv run pytest -v tests

实践建议:日常提交前跑 make validate,钩子改动了文件则复查改动并重跑直至干净;需要只读验证时用 make check;需要一键格式化用 make fix

三、测试:为改动补测试,并理解参考数据的再生成机制

CONTRIBUTING.md 的 "Tests" 部分包含两条规则:

  1. 提交新功能或修复时,请考虑为其添加一个简短的测试;
  2. 当改动会改善转换结果时,必须重新生成并人工审查多份参考文档,参考数据用如下命令再生成:
DOCLING_GEN_TEST_DATA=1 uv run pytest

并且,所有修改参考测试数据的 PR 都需要双人评审(double review),以确保不遗漏边界情况。

3.1 DOCLING_GEN_TEST_DATA 在源码中的落点

这个开关由 tests/test_data_gen_flag.py 读取:

GEN_TEST_DATA = TypeAdapter(bool).validate_python(os.getenv("DOCLING_GEN_TEST_DATA", 0))

即默认关闭(0),仅当环境变量显式设为真值时进入"生成模式"。该标志随后被 tests/groundtruth_paths.py 与各后端的测试文件(test_e2e_conversion.pytest_backend_msword.pytest_backend_html.py 等 20 余个)共同消费。

3.2 每个参考文档产出四件套

tests/groundtruth_paths.py 可以看到,每份被转换文档的基准(ground truth)由四个文件组成:

_PAGES_META_SUFFIX = ".pages.meta.json"
_JSON_SUFFIX = ".json"
_MD_SUFFIX = ".md"
_DOCTAGS_SUFFIX = ".doctags.txt"

即页面元信息、Docling JSON 全量结果、Markdown 导出与 DocTags 导出。该文件还维护 OCR 引擎到基准子目录的映射(tesseracteasyocrrapidocrocrmac 等),意味着切换 OCR 引擎会落到不同的参考数据目录,再生成数据时要确认目标引擎目录无误。测试目录的 groundtruth/sources/ 二元结构(如 tests/data/docx 下 33 组 docx 源文件对应 101 个基准产物)就是这套机制的规模体现。

3.3 pytest 的组织与标记

pyproject.tomltestpaths = ["tests"],并声明了六类 marker,按"是否触发模型下载/外部服务"划分测试重量:

  • ml_ocrml_pdf_modelml_vlmml_asr:会下载或执行模型代码的测试;
  • cross_platform:Windows/macOS 上的轻量冒烟测试;
  • external_service:需要真实外部服务、默认排除出 CI 的测试。

因此贡献者本地跑完整套件(make test,等价 uv run pytest -v tests)耗时较长;针对改动做"目标测试"(如 uv run pytest tests/test_backend_html.py)是 CONTRIBUTING.md 与 AGENTS.md 一致推荐的做法。

四、文档:本地预览与 GitHub Pages 部署

Docling 使用 MkDocs 体系编写文档(文档源在 docs/,站点配置为 mkdocs.yml)。CONTRIBUTING.md 给出的两条命令:

启动本地文档服务器:

mkdocs serve

服务默认在 http://localhost:8000 提供,支持随改随看。

将文档推送到 GitHub Pages:

mkdocs gh-deploy

4.1 仓库当前的构建链路补充

需要注意,仓库的 Makefile 已将文档构建演进为"预渲染 + 静态构建"两步(见 Makefile):

make docs-render   # 先预渲染示例 notebook 与 CLI 参考页
make docs-build    # docs-render 之后构建静态站点
make docs-serve    # docs-render 之后本地带热重载地服务

其中 docs-render 依次执行 scripts/render_notebooks.pyscripts/render_cli_reference.py。从 pyproject.tomldocs 依赖组注释可以印证:示例 notebook 与 jupytext 格式的 .py 脚本会在构建期被预渲染到 docs/_generated/examples,CLI 参考页则渲染为 docs/reference/cli.mdmake docs-clean 负责清理这些生成物)。因此,当你修改了示例代码或 CLI 命令定义时,只需在源文件上工作——_generated 产物由脚本重新生成,不应手工编辑。

五、贡献前自检清单

综合 CONTRIBUTING.md 与仓库配置,一次合格的改动在提交前应满足:

  1. 环境与锁文件一致:改动依赖走 uv adduv.lock 由工具更新且 uv lock --locked 可通过(make check 内含此校验);
  2. 风格与类型检查干净:make validate 通过;若钩子改写了文件,复核改动后重跑至干净(对应 CONTRIBUTING.md 的 "Ruff 会因修改文件而失败" 提示);
  3. 模块边界不破坏:make tach 无新增违规,新文件行数不超 1500 行上限;
  4. 行为有测试覆盖:新增/修改了转换逻辑就跑对应 tests/test_backend_*.py 或 e2e 测试;
  5. 参考数据变更受控:确实改变了转换输出时,用 DOCLING_GEN_TEST_DATA=1 uv run pytest 再生成 .pages.meta.json / .json / .md / .doctags.txt 四件套并逐份审查,PR 提交后预留双人评审;
  6. 用户可见行为同步更新 docs/ 中的文档与示例。

另外,CONTRIBUTING.md 指出更广义的贡献规范(社区流程、沟通渠道等)放在 Docling Project 的 community 仓库中维护,本仓库只聚焦开发、风格、测试与文档四类工程实践。

参考

登录后查看全文
热门项目推荐
相关项目推荐