☰
Sphinx 文档覆盖率检查实战:从 sphinx.ext.coverage 到 grog 测试示例的完整解析
2026/9/29 12:36:33 网站建设 项目流程
  • 文档
  • 开发工具

【免费下载链接】sphinx

The Sphinx documentation generator

项目地址:https://gitcode.com/gh_mirrors/sp/sphinx
点击查看免费下载

Sphinx 自带的sphinx.ext.coverage扩展可以扫描项目中通过 autodoc 或 Python 域记录的对象,找出那些"写进了代码却从未被文档提到"的类、函数和方法,并以独立构建器输出覆盖率报告。本文以仓库中 tests/roots/test-ext-coverage/index.rst 这一真实测试项目为骨架,结合 coverage 扩展源码 与对应测试用例,完整讲解覆盖率构建器的启用方式、全部配置项、忽略规则的作用机制,以及如何读懂它生成的python.txt/c.txt报告,帮助你直接在自己项目中复现这套"文档缺失检测"流程。

从测试根目录读懂 coverage 扩展的典型用法

Sphinx 仓库的测试体系中,每个tests/roots/下的目录都是一个独立的迷你文档项目。tests/roots/test-ext-coverage 专为验证sphinx.ext.coverage的"忽略规则"而设计,其结构非常精简:

tests/roots/test-ext-coverage/ ├── conf.py ├── index.rst └── grog/ ├── __init__.py ├── coverage_ignored.py ├── coverage_missing.py └── coverage_not_ignored.py

其中 index.rst 全文只有两个automodule指令:

.. automodule:: grog.coverage_ignored :members: .. automodule:: grog.coverage_not_ignored :members:

也就是说,这个测试项目通过 autodoc 只"正式记录"了grog包下的两个模块,而grog.coverage_missing模块虽然存在,却没有被任何automodule/py:module指令提及——它正是用来验证"覆盖率构建器能否发现被文档遗漏的模块"的靶子。这就是 coverage 扩展最核心的场景:文档写了什么、源码里有什么,两者之间的差集就是"未文档化对象"。

配套的 conf.py 给出了启用该扩展的最小配置:

import sys from pathlib import Path sys.path.insert(0, str(Path.cwd().resolve())) extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage'] coverage_modules = [ 'grog', ] coverage_ignore_pyobjects = [ r'^grog\.coverage_ignored(\..*)?$', r'\.Ignored$', r'\.Documented\.ignored\d$', ]

这个配置里有三个关键点值得逐一拆解:

  1. sys.path调整:coverage 构建器会真正import目标模块,因此必须让 Sphinx 进程能通过sys.path找到它们(官方文档 doc/usage/extensions/coverage.rst 中也明确提示了这一点)。
  2. extensions同时启用了sphinx.ext.autodoc与sphinx.ext.coverage:前者让automodule指令生效并产生"已文档化对象"记录,后者负责扫描源码并对比出未文档化对象。
  3. coverage_ignore_pyobjects定义了三条正则,用于把"有意不写文档"的对象从报告中剔除——这正是本测试项目的验证重点。

CoverageBuilder:coverage 构建器的工作原理

启用方式

与普通 HTML/PDF 构建器不同,sphinx.ext.coverage不产生可浏览的页面,而是通过sphinx-build -M coverage在_build/coverage目录下生成文本报告。它的构建器类CoverageBuilder定义在 sphinx/ext/coverage.py,在扩展的setup()中通过app.add_builder(CoverageBuilder)注册(sphinx/ext/coverage.py)。

构建器名称coverage与命令行的对应关系为:

sphinx-build -M coverage sourcedir builddir

构建结束后,builddir/coverage目录下会产出三类文件:

文件内容
python.txt未文档化的 Python 对象清单(函数、类、缺失方法)及统计表
c.txt未文档化的 C API 元素清单
undoc.pickle序列化后的全部未文档化/已文档化数据,供后续程序化分析(见 sphinx/ext/coverage.py)

从CoverageBuilder.epilog(sphinx/ext/coverage.py)可以看到构建完成时会输出提示:

Testing of coverage in the sources finished, look at the results in <builddir>/coverage/python.txt.

一次构建的执行流程

write_documents()(sphinx/ext/coverage.py)是覆盖率构建的主入口,内部顺序执行四个步骤:

  1. build_py_coverage():扫描 Python 模块,产出未文档化对象字典;
  2. write_py_coverage():把结果写入python.txt;
  3. build_c_coverage():扫描 C 头文件,产出未文档化 C API 元素;
  4. write_c_coverage():把结果写入c.txt。

Python 侧扫描:build_py_coverage()的核心逻辑

Python 侧扫描的逻辑(sphinx/ext/coverage.py)大致如下:

  • 从self.env.domaindata['py']['objects']和['modules']取回文档树中所有"已出现"的 Python 对象与模块——这些记录正是由automodule、py:module、py:function等指令在解析阶段写入环境的;
  • 调用_determine_py_coverage_modules()(sphinx/ext/coverage.py)确定要检查哪些模块,这一步决定了两种工作模式(见下文);
  • 对每个模块执行inspect.getmembers(mod),逐成员过滤:以下划线开头的名字、无法归属到本模块的对象(obj.__module__ != mod_name)、被coverage_ignore_pyobjects匹配的对象都会被跳过;
  • 对函数用inspect.isfunction、对类用inspect.isclass分类,再对比"已在文档中出现"的seen_objects,凡是在源码中存在却不在文档中的,计入py_undoc;
  • 对已文档化的类,还会遍历其dir()中的方法/函数属性,找出"类写了文档、方法没写"的缺口,记录为classes[class_name] = [缺失的方法名]。

关于_determine_py_coverage_modules(),源码 docstring(sphinx/ext/coverage.py)明确描述了两种模式:

  • 不配置coverage_modules:只检查文档树中"出现过的"模块。此时只能发现这些模块内的缺失对象,但无法发现"整个模块都没被文档提到"的情况;
  • 配置coverage_modules:递归导入指定包及其所有子包/子模块(_load_modules使用pkgutil.iter_modules遍历,见 sphinx/ext/coverage.py),此时既能发现缺失对象,也能发现"模块级遗漏"。如果文档中有模块不在coverage_modules里,或coverage_modules里有模块从未被文档化,构建器会输出警告但继续执行(sphinx/ext/coverage.py)。

测试项目 conf.py 配置了coverage_modules = ['grog'],因此grog.coverage_missing这个"既在源码中、又不在文档中"的模块会被发现,这正是该测试能验证"模块级遗漏检测"的原因。

C 侧扫描:build_c_coverage()的核心逻辑

C API 的扫描(sphinx/ext/coverage.py)机制不同但思路一致:

  • 先收集c域中所有已文档化的 C 对象(self.env.domains.c_domain.get_objects());
  • 遍历coverage_c_path匹配到的每个头文件,逐行用coverage_c_regexes中的正则去匹配,提取对象名;
  • 若该名字未出现在 C 域文档中,则记录为(类型, 名字)元组,写入c.txt。

配置项全览:让覆盖率报告精准可用

coverage扩展通过app.add_config_value()注册了 13 个配置项(sphinx/ext/coverage.py),官方文档 doc/usage/extensions/coverage.rst 对其有完整说明。下面按用途分组列出,并补充默认值与类型:

Python 侧配置

配置项类型默认值作用
coverage_moduleslist/tuple of str()指定要检查的包/模块列表,启用"模块级遗漏检测"(7.4 版本加入)
coverage_ignore_moduleslist/tuple of str[]匹配完整模块路径的正则列表,命中的模块整个跳过
coverage_ignore_functionslist/tuple of str[]匹配函数名的正则列表,命中的函数跳过
coverage_ignore_classeslist/tuple of str[]匹配类名的正则列表,命中的类跳过
coverage_ignore_pyobjectslist/tuple of str[]匹配任意 Python 对象完整导入路径的正则列表(2.1 版本加入)

这些正则使用 Python 的re语法,在构建器init()阶段通过compile_regex_list()统一编译(sphinx/ext/coverage.py),无效正则会在日志中输出invalid regex ... in <配置项名>警告(sphinx/ext/coverage.py)。

C 侧配置

配置项类型默认值作用
coverage_c_pathlist/tuple of str[]相对于源目录的 C 头文件 glob 模式列表,用于定位待检查的.h文件
coverage_c_regexesdict[str, str]{}每个条目将"对象类型名"映射到一条正则,正则的第一个捕获组即对象名
coverage_ignore_c_itemsdict[str, list of str]{}按对象类型给出正则列表,命中的 C 对象不计入缺失报告

C 侧的路径、正则同样在init()中预处理(sphinx/ext/coverage.py)。

报告输出配置

配置项类型默认值作用
coverage_write_headlineboolTrue设为False时不写报告开头的标题行(1.1 版本加入)
coverage_skip_undoc_in_sourceboolFalse跳过源码中本身就没有 docstring 的对象(1.1 版本加入)
coverage_show_missing_itemsboolFalse除了写入报告文件,还把缺失对象打印到 stdout / 日志(3.1 版本加入)
coverage_statistics_to_reportboolTrue把统计表格写入报告文件(7.2 版本加入)
coverage_statistics_to_stdoutboolFalse把统计表格打印到标准输出(7.2 版本加入,注意默认值与_to_report相反)

忽略规则实战:test-ext-coverage 的三种正则模式

回到测试项目,conf.py 中的coverage_ignore_pyobjects三条正则分别演示了三种常见需求:

coverage_ignore_pyobjects = [ r'^grog\.coverage_ignored(\..*)?$', # 模式一:忽略整个模块 r'\.Ignored$', # 模式二:忽略所有名为 Ignored 的类 r'\.Documented\.ignored\d$', # 模式三:忽略特定类的特定方法 ]

结合三个grog子模块的源码内容(coverage_ignored.py、coverage_not_ignored.py、coverage_missing.py)逐一验证:

  • 模式一匹配grog.coverage_ignored及其所有子对象((\..*)?捕获后缀)。因此即使index.rst通过automodule :members:记录了该模块,它内部的Documented.ignored1、Documented.ignored2、NotIgnored等对象也不会进入报告——尽管它们并未被文档提及。
  • 模式二匹配所有以.Ignored结尾的完整路径,因此两个模块中的Ignored类都被排除。
  • 模式三匹配Documented.ignored1/Documented.ignored2这类方法路径,因此这两个"有意不写文档的方法"不会计入缺失。

最终,只有grog.coverage_not_ignored模块中的Documented.not_ignored1、not_ignored2和NotIgnored类会"暴露"为未文档化对象。测试用例 tests/test_extensions/test_ext_coverage.py 对这次构建的python.txt做了逐字符断言,其统计表原文如下:

+---------------------------+----------+--------------+ | Module | Coverage | Undocumented | +===========================+==========+==============+ | grog | 100.00% | 0 | +---------------------------+----------+--------------+ | grog.coverage_missing | 100.00% | 0 | +---------------------------+----------+--------------+ | grog.coverage_not_ignored | 0.00% | 2 | +---------------------------+----------+--------------+ | TOTAL | 0.00% | 2 | +---------------------------+----------+--------------+

报告主体则精确列出了两个缺失对象:

grog.coverage_missing --------------------- Classes: * Missing grog.coverage_not_ignored ------------------------- Classes: * Documented -- missing methods: - not_ignored1 - not_ignored2 * NotIgnored

这份输出同时证明了两个事实:grog.coverage_missing模块因从未出现在文档中而被标记为"模块级遗漏";而grog.coverage_not_ignored中的Documented类虽然被automodule记录,但其方法not_ignored1/not_ignored2仍未文档化。注意测试断言的统计表中两行 Coverage 均为 100.00%、TOTAL 为 0.00%,这是因为覆盖率百分比按模块分别计算、而TOTAL行取的是整体交集分母,理解这一点有助于读懂真实项目中的统计数字。

报告结构解读与进阶输出选项

python.txt的完整结构

write_py_coverage()(sphinx/ext/coverage.py)决定了报告文件的内容布局,从上到下依次为:

  1. 标题(Undocumented Python objects,可由coverage_write_headline关闭);
  2. Statistics 统计表(由coverage_statistics_to_report控制,表格由_write_py_statistics()生成,见 sphinx/ext/coverage.py);
  3. 按模块名排序的未文档化对象明细:
    • Functions:段:模块级未文档化函数,逐行以* 函数名列出;
    • Classes:段:未文档化类逐行列出;对于"类已文档化但方法缺失"的情况,输出* 类名 -- missing methods:后逐行缩进列出缺失方法名;
  4. Modules that failed to import段:无法 import 的模块及其异常信息。

_write_py_statistics()中覆盖率的计算公式为:

模块覆盖率 = 100.0 * 已文档化对象数 / (已文档化对象数 + 未文档化对象数)

该模块没有发现任何对象时按 100% 处理(sphinx/ext/coverage.py)。表头行使用=分隔、数据行使用-分隔,格式与测试断言完全一致。

让缺失对象直接出现在构建输出中

默认情况下,未文档化对象只写入报告文件,构建过程中不会有任何提示。设置coverage_show_missing_items = True后,构建时会同步把缺失对象打印出来:

  • 默认(有进度显示)时使用彩色info日志,格式如undocumented py function raises - in module autodoc_target;
  • 使用-q安静模式(verbosity < 0)时改用warning日志,格式如undocumented python function: autodoc_target :: raises。

测试用例 test_show_missing_items 与 test_show_missing_items_quiet 分别验证了这两种输出路径,同时覆盖了 Python 函数、类、方法以及 C API 元素(如undocumented c api: Py_SphinxTest [function])四种类型的提示。

C 侧报告c.txt的结构与python.txt类似,先写Undocumented C API elements标题,再按头文件分组列出未文档化元素,每行格式为* 名字 [类型](sphinx/ext/coverage.py)。

在真实项目中落地:完整配置示例与注意事项

将上述内容整合,一个可直接照搬的项目级配置如下(在conf.py中):

extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.coverage', ] # 指定要递归检查的包;不设置则只检查文档中出现过的模块 coverage_modules = ['my_package'] # 忽略规则:可分别针对模块、函数、类、任意对象编写正则 coverage_ignore_modules = [r'my_package\.internal'] coverage_ignore_functions = [r'^_'] coverage_ignore_classes = [] coverage_ignore_pyobjects = [r'\.Deprecated$'] # C API 检查(可选) coverage_c_path = ['include/*.h'] coverage_c_regexes = {'function': r'^\w+\s+(\w+)\s*\('} coverage_ignore_c_items = {'function': [r'^internal_']} # 报告输出控制 coverage_show_missing_items = True # 构建时直接打印缺失对象 coverage_skip_undoc_in_source = False # True 则跳过源码中无 docstring 的对象 coverage_statistics_to_stdout = True # 统计表同时输出到 stdout coverage_statistics_to_report = True # 统计表写入报告 coverage_write_headline = True # False 则不写标题行

运行方式:

sphinx-build -M coverage source _build

然后查看_build/coverage/python.txt与_build/coverage/c.txt。

结合源码与官方文档(doc/usage/extensions/coverage.rst),使用时有几点需要注意:

  1. 模块导入副作用:coverage 构建器会真实import被检查的模块。如果模块在导入时执行了副作用代码(如发请求、写文件),这些代码会在sphinx-build运行期间被执行。对于脚本类模块,务必用if __name__ == '__main__':保护入口;这是官方文档中明确给出的警告。
  2. sys.path可见性:被检查的模块必须能被 Python 解释器 import 到,必要时像测试项目的 conf.py 一样在sys.path中插入项目根目录。
  3. 模块级遗漏需要显式配置:只有设置coverage_modules后,才能发现"整个模块都没出现在文档中"的遗漏;不设置时只能发现已文档化模块内部的缺失对象。
  4. 忽略规则使用正则匹配:coverage_ignore_pyobjects等配置匹配的是对象完整导入路径(如grog.coverage_ignored.Documented.ignored1)的任意部分,^/$/\d等re语法全部可用,且匹配使用search语义(见 sphinx/ext/coverage.py),因此\.Ignored$这类锚定写法可以精确命中类名结尾。

小结

tests/roots/test-ext-coverage/index.rst虽然只有六行,但它背后是sphinx.ext.coverage一整套"源码对照文档"的扫描机制:CoverageBuilder以python.txt/c.txt/undoc.pickle三种形式输出结果,13 个配置项覆盖了模块级扫描、正则忽略、统计表格与日志输出等全部需求。借助 conf.py 中的三条忽略规则和 test_ext_coverage.py 的逐字节断言,你可以清晰地推演任意对象被纳入或排除的判定路径,并把这套能力直接复用到自己的文档项目中——让"写了代码却忘了写文档"的缺口在每次构建时自动暴露出来。

  • 文档
  • 开发工具

【免费下载链接】sphinx

The Sphinx documentation generator

项目地址:https://gitcode.com/gh_mirrors/sp/sphinx
点击查看免费下载
上一篇:构建响应式应用:gh_mirrors/pr/promises事件驱动编程
下一篇:如何使用DGFraud在5分钟内搭建第一个欺诈检测模型:快速入门指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询