Docling 开源贡献实战指南:uv 开发环境、代码风格、参考测试数据与文档工作流
本文基于 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.toml 与 uv.lock 会一并更新。CONTRIBUTING.md 强调不要手改 uv.lock——该文件在 CI 中通过 uv lock --locked 做只读校验(见 Makefile 的 check-all 目标)。
1.4 仓库侧印证:开发依赖组与 workspace
从 pyproject.toml 的 [dependency-groups] 可以看到仓库如何组织开发依赖:
typecheck组:ty、各types-*/*-stubs类型存根;dev组:包含prek>=0.3.10、tach、pytest~=9.0.3、pytest-xdist、nbqa、dprint-py等,正是本文后续各检查工具的安装来源;docs组与examples组:分别用于文档构建与示例运行,默认不参与贡献者环境安装。
同时 pyproject.toml 声明了 uv workspace:
[tool.uv.workspace]
members = ["packages/docling", "packages/docling-client"]
[tool.uv]
package = true
default-groups = "all"
即根项目与 packages/docling、packages/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 安装全部可选格式/模型依赖,并显式排除 docs、examples 两个组——这就是 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 |
清理行尾空白、保证文件以换行结尾 | 限定 docling、tests、perfs、docs/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)、PIE、Q、RUF、S307(禁eval)、ASYNC、UP(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;对 torchvision、transformers、docling_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 tach(Makefile)可随时验证。
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" 部分包含两条规则:
- 提交新功能或修复时,请考虑为其添加一个简短的测试;
- 当改动会改善转换结果时,必须重新生成并人工审查多份参考文档,参考数据用如下命令再生成:
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.py、test_backend_msword.py、test_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 引擎到基准子目录的映射(tesseract、easyocr、rapidocr、ocrmac 等),意味着切换 OCR 引擎会落到不同的参考数据目录,再生成数据时要确认目标引擎目录无误。测试目录的 groundtruth/ 与 sources/ 二元结构(如 tests/data/docx 下 33 组 docx 源文件对应 101 个基准产物)就是这套机制的规模体现。
3.3 pytest 的组织与标记
pyproject.toml 中 testpaths = ["tests"],并声明了六类 marker,按"是否触发模型下载/外部服务"划分测试重量:
ml_ocr、ml_pdf_model、ml_vlm、ml_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.py 与 scripts/render_cli_reference.py。从 pyproject.toml 的 docs 依赖组注释可以印证:示例 notebook 与 jupytext 格式的 .py 脚本会在构建期被预渲染到 docs/_generated/examples,CLI 参考页则渲染为 docs/reference/cli.md(make docs-clean 负责清理这些生成物)。因此,当你修改了示例代码或 CLI 命令定义时,只需在源文件上工作——_generated 产物由脚本重新生成,不应手工编辑。
五、贡献前自检清单
综合 CONTRIBUTING.md 与仓库配置,一次合格的改动在提交前应满足:
- 环境与锁文件一致:改动依赖走
uv add,uv.lock由工具更新且uv lock --locked可通过(make check内含此校验); - 风格与类型检查干净:
make validate通过;若钩子改写了文件,复核改动后重跑至干净(对应 CONTRIBUTING.md 的 "Ruff 会因修改文件而失败" 提示); - 模块边界不破坏:
make tach无新增违规,新文件行数不超 1500 行上限; - 行为有测试覆盖:新增/修改了转换逻辑就跑对应
tests/test_backend_*.py或 e2e 测试; - 参考数据变更受控:确实改变了转换输出时,用
DOCLING_GEN_TEST_DATA=1 uv run pytest再生成.pages.meta.json/.json/.md/.doctags.txt四件套并逐份审查,PR 提交后预留双人评审; - 用户可见行为同步更新 docs/ 中的文档与示例。
另外,CONTRIBUTING.md 指出更广义的贡献规范(社区流程、沟通渠道等)放在 Docling Project 的 community 仓库中维护,本仓库只聚焦开发、风格、测试与文档四类工程实践。
参考
- 贡献指南原文:CONTRIBUTING.md
- AI 协作补充指引:AGENTS.md
- 检查与构建目标:Makefile
- 依赖组与工具配置:pyproject.toml
- 钩子定义:.pre-commit-config.yaml
- 模块分层约束:tach.toml
- 测试数据机制:tests/test_data_gen_flag.py、tests/groundtruth_paths.py
- 维护脚本:scripts/check_max_lines.py、scripts/check_tach_module_coverage.py、scripts/render_notebooks.py、scripts/render_cli_reference.py
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00