sphinx.ext.doctest – 文档中的代码片段测试

在文档中包含代码片段并演示其执行结果通常很有帮助。但重要的是要确保文档与代码保持最新。

此扩展允许您以自然的方式测试文档中的此类代码片段。如果您按照此处所示标记代码块,则 doctest 构建器将收集它们并将其作为 doctest 测试运行。

在每个文档中,您可以将每个代码片段分配给一个。每个组包含

  • 零个或多个设置代码块(例如,导入要测试的模块)

  • 一个或多个测试

使用 doctest 构建器构建文档时,将为每个文档收集组并按顺序运行,首先执行设置代码块,然后按其在文件中出现的顺序执行测试块。

有两种类型的测试块

  • doctest 样式块通过交错 Python 代码(包括解释器提示)和输出,模拟交互式会话。

  • 代码-输出样式块由一段普通的 Python 代码组成,以及可选的该代码的输出。

指令

下面的group 参数解释如下:如果为空,则将块分配给名为 default 的组。如果为 *,则将块分配给所有组(包括 default 组)。否则,它必须是组名称的逗号分隔列表。

.. testsetup:: [group]

设置代码块。此代码不会在其他构建器的输出中显示,但在其所属组的 doctest 之前执行。

.. testcleanup:: [group]

清理代码块。此代码不会在其他构建器的输出中显示,但在其所属组的 doctest 之后执行。

版本 1.1 中新增。

.. doctest:: [group]

doctest 样式的代码块。您可以使用标准 doctest 标志来控制如何将实际输出与您提供的输出进行比较。标志的默认集由 doctest_default_flags 配置变量指定。

此指令支持五个选项

  • hide,一个标志选项,隐藏其他构建器中的 doctest 块。默认情况下,它显示为突出显示的 doctest 块。

  • options,一个字符串选项,可用于提供应用于测试中每个示例的 doctest 标志的逗号分隔列表。(您仍然可以在每个示例中提供显式标志,使用 doctest 注释,但它们也会显示在其他构建器中。)

  • pyversion,一个字符串选项,可用于指定要测试的示例所需的 Python 版本。例如,在以下情况下,仅对大于 3.12 的 Python 版本测试示例

    .. doctest::
       :pyversion: > 3.12
    

    支持以下操作数

    • ~=:兼容版本声明

    • ==:版本匹配声明

    • !=:版本排除声明

    • <=>=:包含有序比较声明

    • <>:排除非有序比较声明

    • ===:任意相等声明。

    pyversion 选项遵循 PEP-440:版本说明符

    版本 1.6 中新增。

    版本 1.7 中更改: 支持 PEP-440 操作数和表示法

  • trim-doctest-flagsno-trim-doctest-flags,一个标志选项,doctest 标志(类似于 # doctest: FLAG, ... 的注释)在行尾和 <BLANKLINE> 标记将被单独删除(或不删除)。默认值为 trim-doctest-flags

请注意,与标准 doctest 一样,您必须使用 <BLANKLINE> 来表示预期输出中的空行。在构建演示输出(HTML、LaTeX 等)时会删除 <BLANKLINE>

此外,您还可以提供内联 doctest 选项,就像在 doctest 中一样

>>> datetime.date.now()   # doctest: +SKIP
datetime.date(2008, 1, 1)

运行测试时将尊重它们,但会从演示输出中删除。

.. testcode:: [group]

代码-输出样式测试的代码块。

此指令支持三个选项

  • hide,一个标志选项,隐藏其他构建器中的代码块。默认情况下,它显示为突出显示的代码块。

  • trim-doctest-flagsno-trim-doctest-flags,一个标志选项,doctest 标志(类似于 # doctest: FLAG, ... 的注释)在行尾和 <BLANKLINE> 标记将被单独删除(或不删除)。默认值为 trim-doctest-flags

注意

testcode 块中的代码始终一次性全部执行,无论它包含多少个语句。因此,不会为裸表达式生成输出 - 使用 print。示例

.. testcode::

   1+1         # this will give no output!
   print(2+2)  # this will give output

.. testoutput::

   4

此外,请注意,由于 doctest 模块不支持在同一个代码片段中混合常规输出和异常消息,因此这也适用于 testcode/testoutput。

.. testoutput:: [group]

最后一个 testcode 块的对应输出或异常消息。

此指令支持四个选项

  • hide,一个标志选项,隐藏其他构建器中的输出块。默认情况下,它显示为没有突出显示的文字块。

  • options,一个字符串选项,可用于提供 doctest 标志(逗号分隔),就像在普通 doctest 块中一样。

  • trim-doctest-flagsno-trim-doctest-flags,一个标志选项,doctest 标志(类似于 # doctest: FLAG, ... 的注释)在行尾和 <BLANKLINE> 标记将被单独删除(或不删除)。默认值为 trim-doctest-flags

示例

.. testcode::

   print('Output     text.')

.. testoutput::
   :hide:
   :options: -ELLIPSIS, +NORMALIZE_WHITESPACE

   Output text.

以下是指令用法的示例。通过 doctest 进行的测试与通过 testcodetestoutput 进行的测试是等价的。

The parrot module
=================

.. testsetup:: *

   import parrot

The parrot module is a module about parrots.

Doctest example:

.. doctest::

   >>> parrot.voom(3000)
   This parrot wouldn't voom if you put 3000 volts through it!

Test-Output example:

.. testcode::

   parrot.voom(3000)

This would output:

.. testoutput::

   This parrot wouldn't voom if you put 3000 volts through it!

有条件地跳过测试

skipif(一个字符串选项)可以用来有条件地跳过指令。例如,当需要根据环境(硬件、网络/VPN、可选依赖项或依赖项的不同版本)运行不同的测试集时,这可能很有用。skipif 选项受所有 doctest 指令支持。以下是使用不同指令时 skipif 的典型用例

  • testsetuptestcleanup

    • 有条件地跳过测试设置和/或清理

    • 根据环境自定义设置/清理代码

  • doctest

    • 有条件地跳过测试及其输出验证

  • testcode

    • 有条件地跳过测试

    • 根据环境自定义测试代码

  • testoutput

    • 有条件地跳过已跳过测试的输出断言

    • 根据环境预期不同的输出

skipif 选项的值将作为 Python 表达式进行评估。如果结果为真值,则指令将从测试运行中省略,就像它根本不存在于文件中一样。

无需重复表达式,可以使用 doctest_global_setup 配置选项将其赋值给一个变量,然后可以使用该变量。

以下是一个示例,如果未安装 Pandas,则跳过某些测试

conf.py
extensions = ['sphinx.ext.doctest']
doctest_global_setup = '''
try:
    import pandas as pd
except ImportError:
    pd = None
'''
contents.rst
.. testsetup::
   :skipif: pd is None

   data = pd.Series([42])

.. doctest::
   :skipif: pd is None

   >>> data.iloc[0]
   42

.. testcode::
   :skipif: pd is None

   print(data.iloc[-1])

.. testoutput::
   :skipif: pd is None

   42

配置

doctest 扩展使用以下配置值

doctest_default_flags

默认情况下,启用以下选项:

  • ELLIPSIS,允许您在预期输出中放置省略号,以匹配实际输出中的任何内容;

  • IGNORE_EXCEPTION_DETAIL,导致忽略异常名称中左侧最靠近冒号的所有内容以及任何模块信息;

  • DONT_ACCEPT_TRUE_FOR_1,拒绝输出中出现“True”的情况,而“1”则被接受——接受这种替换的默认行为是 Python 2.2 之前的遗留行为。

版本 1.5 中新增。

doctest_show_successes

默认为 True。控制是否报告成功。

对于包含许多 doctest 的项目,将其设置为 False 可能很有用,以便仅突出显示失败。

版本 7.2 中新增。

doctest_path

一个目录列表,当使用 doctest 构建器时,这些目录将添加到 sys.path 中。(确保它包含绝对路径。)

doctest_global_setup

Python 代码,其处理方式如同放置在每个要测试的文件和每个组的 testsetup 指令中一样。您可以使用它来导入 doctest 中始终需要的模块。

版本 0.6 中新增。

doctest_global_cleanup

Python 代码,其处理方式如同放置在每个要测试的文件和每个组的 testcleanup 指令中一样。您可以使用它来删除测试留下的任何临时文件。

版本 1.1 中新增。

doctest_test_doctest_blocks

如果这是一个非空字符串(默认值为 'default'),则标准的 reStructuredText doctest 块也将被测试。它们将被分配给给定的组名称。

reStructuredText doctest 块只是将 doctest 放入自己的段落中,如下所示:

Some documentation text.

>>> print(1)
1

Some more documentation text.

(请注意,没有使用特殊的 :: 来引入 doctest 块;docutils 从开头的 >>> 识别它们。此外,未使用额外的缩进,尽管这样做也没有坏处。)

如果此值保留为其默认值,则上述代码段将由 doctest 构建器解释为如下所示:

Some documentation text.

.. doctest::

   >>> print(1)
   1

Some more documentation text.

此功能使您可以轻松地测试包含在 autodoc 扩展中的文档字符串中的 doctest,而无需使用特殊指令进行标记。

但请注意,您不能在 reStructuredText doctest 块中包含空行。它们将被解释为一个块结束,另一个块开始。此外,仅在 doctest 块中才能删除 <BLANKLINE># doctest: 选项,尽管您可以在所有包含 Python 控制台内容的代码块中设置 trim_doctest_flags 以实现这一点。