ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Sphinx 中 automodule 与 doctest 的协同:如何让被自动生成的模块 docstring 里的 doctest 被测试、被定位

Sphinx 中 automodule 与 doctest 的协同:如何让被自动生成的模块 docstring 里的 doctest 被测试、被定位 文档开发工具【免费下载链接】sphinxThe Sphinx documentation generator项目地址https://gitcode.com/gh_mirrors/sp/sphinx点击查看免费下载导读在 Sphinx 文档工程中sphinx.ext.autodoc的automodule指令负责把模块 docstring 与成员文档自动拉进文档而sphinx.ext.doctest负责把文档中的代码片段当作真实测试执行。当这两者相遇——即被automodule生成的 docstring 内容里恰好含有 doctest 代码块时——Sphinx 必须解决一个关键问题测试会被执行吗失败时如何报告、如何精确定位到源文件本文以 Sphinx 仓库中 tests/roots/test-ext-doctest-with-autodoc 测试夹具为切入点完整剖析automoduledoctest组合下的测试收集、源码定位与失败上报机制并给出可直接复用的配置与排查方法。一、问题场景docstring 里的 doctest 需要被测试与定位先看本次分析的关联文档 tests/roots/test-ext-doctest-with-autodoc/dir/inner.rst全文仅 3 行 dir/inner.rst:1 .. automodule:: dir.bar :members:该文件做了三件事在文档正文写了一个普通 doctest 块内容是 dir/inner.rst:1——它故意预期失败用来验证失败报告能否准确指向dir/inner.rst的第 1 行使用.. automodule:: dir.bar引入模块dir.bar的文档通过:members:选项要求 autodoc 同时展开模块内的成员对象类、函数、属性等的文档。而 tests/roots/test-ext-doctest-with-autodoc/dir/bar.py 中模块的 docstring 也是精心设计的“错误”样例 dir/bar.py:2也就是说dir.bar模块 docstring 里同样包含一个预期失败的 doctest。同一测试夹具的 index.rst 还组合了foo模块.. automodule:: foo :members: index.rst:4foo.py 的 docstring 同样是 foo.py:3。因此这个夹具共制造了 4 个“必定失败”的测试点覆盖两类来源文档正文中手写的 doctest 块inner.rst:1、index.rst:4由automodule生成的模块 docstring doctestdir/bar.py:2、foo.py:3。这套夹具的目的是验证无论 doctest 来自正文还是来自 autodoc 展开的 docstringSphinx 都能收集到、执行它并且在失败时给出正确的源文件路径与行号信息。二、底层原理doctest builder 如何收集两类测试2.1 从 doctree 里“捞”测试节点sphinx.ext.doctest实现的DocTestBuilder定义于 sphinx/ext/doctest.py在test_doc()中遍历每个文档的 doctree通过doctree.findall(condition)收集测试节点def _condition_default(node): return ( isinstance(node, (nodes.literal_block, nodes.comment)) and testnodetype in node )也就是说凡是带有testnodetype标记的literal_block或comment节点都会被当作测试代码。automodule生成的 docstring 内容在 autodoc 处理时会转换成普通文档节点若 docstring 中含有前缀的 doctest 片段它同样会被标记并进入 doctree——因此docstring 中的 doctest 与正文 doctest 在收集阶段走的是同一条路径。2.2 逐组执行的测试流程收集到的节点按“组”group归类默认组名为default。每个TestGroup维护setup、tests、cleanup三段见 sphinx/ext/doctest.py 中的TestGroup.add_code()。执行时按test_group()的流程推进先执行组内所有testsetup代码再逐条运行doctest/testcodetestoutput测试最后执行testcleanup清理代码。setup 失败时整个组被跳过测试失败时累计失败计数并可在doctest_fail_fast打开时提前终止。三、核心难点docstring doctest 的源文件与行号定位docstring 中的 doctest 与正文 doctest 最大的差异在于来源位置正文代码块的source直接指向.rst文件而 docstring 代码块的source会被 docutils 标记为形如:docstring of 模块名:行号的形式。为此DocTestBuilder专门实现了两个方法get_filename_for_node()优先从node.source提取真实文件路径即bar.py/foo.py仅在解析失败时回退到文档自身路径env.doc2path(docname)get_line_number()如果节点来自 docstring:docstring of 出现在node.source中由于 docutils 只记录了相对 docstring 字符串的行号而非相对整个源文件的行号方法会返回None——这也是为什么测试断言中 docstring 用例的失败报告是File dir/bar.py, line ?, in default行号用?占位。正是这两点决定了最终失败报告的形态也正是inner.rst夹具中故意制造“错误” doctest 的验证目标。四、失败上报的验证测试如何证明“定位正确”Sphinx 仓库的测试 中test_reporting_with_autodoc用pytest.mark.sphinx(doctest, testrootext-doctest-with-autodoc)指定本夹具构建 doctest 输出然后断言失败报告assert File dir/inner.rst, line 1, in default in failures assert File dir/bar.py, line ?, in default in failures assert File foo.py, line ?, in default in failures assert File index.rst, line 4, in default in failures四条断言对应四类来源验证了来源失败报告定位精度正文 doctestinner.rst第 1 行File dir/inner.rst, line 1精确到文件与行号正文 doctestindex.rst第 4 行File index.rst, line 4精确到文件与行号dir.bar模块 docstringFile dir/bar.py, line ?精确到源文件行号未知foo模块 docstringFile foo.py, line ?精确到源文件行号未知同文件中的test_doctest_block_group_name还参数化验证了当doctest_test_doctest_blocks配置为自定义组名如CustomGroupName时所有失败报告的in default会相应变为in CustomGroupName——说明组名机制同样作用于 docstring 来源的 doctest。五、工程落地如何在你的项目里复现这套组合要在自己的文档项目中启用“docstring 里的 doctest 也会被执行”只需三步。5.1 启用两个扩展在 conf.py 中同时开启autodoc与doctest该夹具的完整配置如下import sys from pathlib import Path sys.path.insert(0, str(Path.cwd().resolve())) project test project for doctest autodoc reporting extensions [sphinx.ext.autodoc, sphinx.ext.doctest]注意sys.path.insert(0, ...)因为automodule需要把dir.bar、foo作为可导入模块doctestbuilder 也会把doctest_path指定的目录加入sys.path。本夹具直接把项目根目录插入sys.path以保证两个模块可被导入。5.2 在文档中使用 automodule在.rst中写入.. automodule:: dir.bar :members::members:是 autodoc 的关键选项表示不仅输出模块 docstring还展开模块内所有成员函数、类、方法、属性的文档——成员各自的 docstring 中的 doctest 同样会被doctestbuilder 收集。5.3 运行 doctest buildersphinx-build -b doctest srcdir outdir执行后失败信息会被写入输出目录下的output.txt见DocTestBuilder.init()中对outdir/output.txt的写入同时按_warn_out的分流逻辑当verbosity 0时作为 warning 输出否则作为普通信息打印。5.4 相关配置项速查与本文场景强相关的配置项全部在 sphinx/ext/doctest.py 的setup()中注册配置项默认值说明doctest_test_doctest_blocksdefault非空字符串时连标准 reST doctest 块也纳入测试并归入指定组doctest_global_setup对每个文件、每个组预先执行的 Python 代码如全局 importdoctest_global_cleanup对每个文件、每个组在测试后执行的清理代码doctest_default_flagsDONT_ACCEPT_TRUE_FOR_1 \| ELLIPSIS \| IGNORE_EXCEPTION_DETAIL默认 doctest 比较标志位doctest_fail_fastFalse首个失败后是否立即退出doctest_path()构建 doctest 时追加到sys.path的目录序列此外doctest指令还支持:pyversion:PEP-440 版本规范不满足则跳过、:skipif:条件跳过、:trim-doctest-flags:/:no-trim-doctest-flags:是否剥离# doctest:标志注释与BLANKLINE标记等选项可用于对单个 docstring 测试做精细化控制。六、注意事项与边界docstring 测试的行号会缺失如get_line_number()注释所述docutils 对 docstring 只记录相对字符串的行号Sphinx 目前无法还原为源文件内的绝对行号故失败报告呈现line ?。这属于已知行为不属于定位 bug——正文 doctest 不受影响仍是精确行号。docstring 必须是可导入模块的 docstringautomodule依赖 Python 导入机制若模块导入失败依赖缺失、sys.path配置错误autodoc 会输出错误而不是测试失败需先保证sphinx-build进程能import目标模块。组名影响定位信息automodule展开的 docstring 测试默认归入default组通过doctest_test_doctest_blocks指定组名后失败报告的in default会随组名变化排查时需对照当前配置。两类测试的定位差异是特性而非缺陷正文 doctest 面向“可读文档中的示例”docstring doctest 面向“API 文档中的内嵌示例”Sphinx 对两者统一执行、统一汇报仅行号精度不同设计上完全自洽。七、小结automodule与doctest的组合让 Sphinx 成为真正“文档即测试”的工具automodule展开的模块 docstring 中的示例会被DocTestBuilder与正文 doctest 一并收集、分组、执行失败时按源文件报告正文 doctest 可精确定位到.rst的行号docstring doctest 则定位到源文件行号以?表示。这一机制由 tests/roots/test-ext-doctest-with-autodoc 夹具与 tests/test_extensions/test_ext_doctest.py 中的断言完整固化你可以把它当作最小复现模板在自己的文档项目中验证 autodoc doctest 的协同行为。赞分享文档开发工具【免费下载链接】sphinxThe Sphinx documentation generator项目地址https://gitcode.com/gh_mirrors/sp/sphinx点击查看免费下载相关推荐深入解析 pwntools 测试示例模块testexample 与 Sphinx 文档驱动的 Doctest 体系深入解析 pwntools 测试示例模块testexample 与 Sphinx 文档驱动的 Doctest 体系 导读 本文以 pwntools 仓库中的网络安全渗透测试逆向工程自定义doctest报告器终极指南如何生成XML、JUnit等格式的测试报告自定义doctest报告器终极指南如何生成XML、JUnit等格式的测试报告 想要让你的C测试报告更专业、更易于集成到CI/CD流水线中吗 doctes测试开发工具深度解析开源围棋分析平台构建高效智能棋谱分析系统的完整实战指南深度解析开源围棋分析平台构建高效智能棋谱分析系统的完整实战指南 LizzieYzy作为一款基于Java开发的 围棋AI分析工具 在经典Lizzie项目基础上测试开发工具上一篇Adobe-GenP 3.0终极指南三步免费激活Photoshop等Adobe全家桶下一篇高效自动化配置Adobe Creative Cloud工具Adobe-GenP 3.0全面解析与实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表