[在
wim正确的回答基础上,进一步解释为什么会失败,并查看底层
doctest
语义。]
这个例子失败了,因为它使用了PS2语法(...
)而不是PS1语法(>>>
)在单独的简单语句前面。
将...
更改为>>>
:
class Dummy(object):
'''Dummy: demonstrates a doctest problem
>>> from StringIO import StringIO
>>> s = StringIO()
>>> print("s is created")
s is created
'''
if __name__ == "__main__":
import doctest
doctest.testmod()
现在,已经更正的示例被重命名为
doctest_pass.py
,可以无误地运行。它不会产生任何输出,这意味着所有的测试都通过了。
% src/doctest_pass.py
为什么
>>>
语法是正确的?Python文档测试库参考中的
25.2.3.2. 如何识别文档字符串示例?应该是找到答案的地方,但它对这个语法并不十分清楚。Doctest通过扫描文档字符串来查找“示例”。在看到PS1字符串
>>>
时,它会将该字符串到行尾之间的所有内容作为示例。它还将任何以PS2字符串
...
开头的后续行附加到示例中(请参阅:
doctest.DocTestParser
类中的_EXAMPLE_RE,第584-595行)。它会将后续行,直到下一个空行或以PS1字符串开头的行,作为预期输出。
Doctest将每个示例编译为Python的“交互式语句”,使用内置函数compile()
在exec
语句中进行编译(参见:doctest.DocTestRunner.__run()
,第1314-1315行)。
“交互式语句”是以换行符结尾的语句列表或复合语句。复合语句,例如if
或try
语句,“一般而言,[跨越]多行,尽管在简单版本中,整个复合语句可以包含在一行中。”以下是一个多行复合语句的示例:
if 1 > 0:
print("As expected")
else:
print("Should not happen")
一个语句列表是一条或多条
简单语句在同一行上,用分号隔开。
from StringIO import StringIO
s = StringIO(); print("s is created")
因为一个示例中有三个简单语句且没有分号分隔符,所以问题的doctest失败了。将PS2字符串更改为PS1字符串会成功,因为它将docstring转换为三个示例序列,每个示例都有一个简单语句。尽管这三行代码一起工作来设置一个功能的测试,但它们不是单个测试夹具。它们是三个测试,其中两个设置状态但实际上并没有测试主要功能。
顺便说一下,您可以使用-v标志看到doctest识别的示例数。请注意,它说:“__main__.Dummy中的3个测试”。人们可能认为这三行代码是一个测试单元,但doctest看到三个示例。前两个示例没有预期输出。当示例执行并生成没有输出时,这被视为“通过”。
% src/doctest_pass.py -v
Trying:
from StringIO import StringIO
Expecting nothing
ok
Trying:
s = StringIO()
Expecting nothing
ok
Trying:
print("s is created")
Expecting:
s is created
ok
1 items had no tests:
__main__
1 items passed all tests:
3 tests in __main__.Dummy
3 tests in 2 items.
3 passed and 0 failed.
Test passed.
在单个文档字符串中,示例按顺序执行。每个示例的状态更改会保留给同一文档字符串中的后续示例。因此,
import
语句定义了模块名称,
s =
赋值语句使用该模块名称并定义变量名称,依此类推。当doctest文档
25.2.3.3. What’s the Execution Context?间接披露时,它说,“示例可以自由地使用...在运行的文档字符串中先前定义的名称。”
该部分中前面的句子“每次doctest找到要测试的文档字符串时,它都会使用M全局变量的浅拷贝,以便... M中的一个测试不能留下碎屑,意外地允许另一个测试工作”有点误导性。确实,M中的一个测试无法影响不同文档字符串中的测试。但是,在单个文档字符串中,先前的测试肯定会留下痕迹,这可能会影响后来的测试。
为什么Python库参考手册中的doctest示例
25.2.3.2. 如何识别文档字符串示例?会显示一个带有
...
语法的示例?该示例展示了一个包含多行的复合语句
if
。第二行及其后续行使用PS2字符串标记。
...
而不是>>>
? - user2357112