为什么导入模块会破坏我的 doctest (Python 2.7)
Why is importing a module breaking my doctest (Python 2.7)
我试图在 class 中的 Python 2.7 程序中使用 StringIO
instance in a doctest。我没有从测试中得到任何输出,而是得到了一个响应 "Got nothing"。
这个简化的测试用例演示了错误:
#!/usr/bin/env python2.7
# encoding: utf-8
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()
预期行为:测试通过。
观察到的行为:测试失败,输出如下:
% ./src/doctest_fail.py
**********************************************************************
File "./src/doctest_fail.py", line 7, in __main__.Dummy
Failed example:
from StringIO import StringIO
s = StringIO()
print("s is created")
Expected:
s is created
Got nothing
**********************************************************************
1 items had failures:
1 of 1 in __main__.Dummy
***Test Failed*** 1 failures.
为什么这个 doctest 失败了?为了能够在我的文档测试中使用类似 StringIO 的功能(带有文件接口的文字字符串),我需要进行哪些更改?
连续行语法 (...
) 混淆了 doctest 解析器。这有效:
#!/usr/bin/env python2.7
# encoding: utf-8
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()
[以 wim 的正确答案为基础,但更多地解释了原因,并查看了底层的 doctest
语义。]
该示例失败,因为它在单独的简单语句前面使用了 PS2 syntax (...
) instead of PS1 语法 (>>>
)。
将...
改为>>>
:
#!/usr/bin/env python2.7
# encoding: utf-8
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
为什么 >>>
语法正确? doctest 的 Python 库参考,25.2.3.2. How are Docstring Examples Recognized? 应该是找到答案的地方,但对这种语法不是很清楚。
Doctest 扫描文档字符串,寻找 "Examples"。在它看到 PS1 字符串 >>>
的地方,它会将从那里到行尾的所有内容作为示例。它还将以下任何以 PS2 字符串 ...
开头的行附加到示例中(参见:_EXAMPLE_RE
in class doctest.DocTestParser
,第 584-595 行)。它采用后续行,直到下一个空白行或以 PS1 字符串开头的行,作为所需输出。
Doctest 使用 compile()
built-in function in an exec
statement 将每个示例编译为 Python "interactive statement"(参见:doctest.DocTestRunner.__run()
,第 1314-1315 行)。
一个“interactive statement" is a statement list ending with a newline, or a Compound Statement。一个复合语句,例如一个if
或try
语句,"in general, […spans] multiple lines, although in simple incarnations a whole compound statement may be contained in one line."这是一个多行复合语句:
if 1 > 0:
print("As expected")
else:
print("Should not happen")
一个语句列表是一个或多个 simple statement 单行,用分号分隔。
from StringIO import StringIO
s = StringIO(); print("s is created")
因此,问题的 doctest 失败了,因为它包含一个包含三个简单语句的示例,并且没有分号分隔符。将 PS2 字符串更改为 PS1 字符串成功,因为它将文档字符串变成了三个示例的序列,每个示例都有一个简单的语句。尽管这三行一起工作以设置一项功能的一项测试,但它们并不是一个单一的测试夹具。它们是三个测试,其中两个设置状态但不真正测试主要功能。
顺便说一下,您可以通过使用 -v
标志查看 doctest
识别的示例数量。请注意,它说的是“3 tests in __main__.Dummy
”。人们可能认为这三行是一个测试单元,但 doctest
看到了三个示例。前两个示例没有预期的输出。当示例执行并且没有生成输出时,这算作 "pass".
% 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? 在说 "examples can freely use … names defined earlier in the docstring being run."
时间接地披露了这一点
该部分的前一句 "each time doctest finds a docstring to test, it uses a shallow copy of M’s globals, so that … one test in M can’t leave behind crumbs that accidentally allow another test to work" 有点误导。确实,M 中的一个测试不会影响另一个文档字符串中的测试。但是,在单个文档字符串中,较早的测试肯定会留下碎屑,这很可能会影响后面的测试。
为什么 Python 文档测试库参考 25.2.3.2. How are Docstring Examples Recognized? 中的示例显示了 ...
语法的示例?该示例显示了一个 if
语句,它是多行的复合语句。第二行和后续行用 PS2 字符串标记。
我试图在 class 中的 Python 2.7 程序中使用 StringIO
instance in a doctest。我没有从测试中得到任何输出,而是得到了一个响应 "Got nothing"。
这个简化的测试用例演示了错误:
#!/usr/bin/env python2.7
# encoding: utf-8
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()
预期行为:测试通过。
观察到的行为:测试失败,输出如下:
% ./src/doctest_fail.py
**********************************************************************
File "./src/doctest_fail.py", line 7, in __main__.Dummy
Failed example:
from StringIO import StringIO
s = StringIO()
print("s is created")
Expected:
s is created
Got nothing
**********************************************************************
1 items had failures:
1 of 1 in __main__.Dummy
***Test Failed*** 1 failures.
为什么这个 doctest 失败了?为了能够在我的文档测试中使用类似 StringIO 的功能(带有文件接口的文字字符串),我需要进行哪些更改?
连续行语法 (...
) 混淆了 doctest 解析器。这有效:
#!/usr/bin/env python2.7
# encoding: utf-8
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()
[以 wim 的正确答案为基础,但更多地解释了原因,并查看了底层的 doctest
语义。]
该示例失败,因为它在单独的简单语句前面使用了 PS2 syntax (...
) instead of PS1 语法 (>>>
)。
将...
改为>>>
:
#!/usr/bin/env python2.7
# encoding: utf-8
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
为什么 >>>
语法正确? doctest 的 Python 库参考,25.2.3.2. How are Docstring Examples Recognized? 应该是找到答案的地方,但对这种语法不是很清楚。
Doctest 扫描文档字符串,寻找 "Examples"。在它看到 PS1 字符串 >>>
的地方,它会将从那里到行尾的所有内容作为示例。它还将以下任何以 PS2 字符串 ...
开头的行附加到示例中(参见:_EXAMPLE_RE
in class doctest.DocTestParser
,第 584-595 行)。它采用后续行,直到下一个空白行或以 PS1 字符串开头的行,作为所需输出。
Doctest 使用 compile()
built-in function in an exec
statement 将每个示例编译为 Python "interactive statement"(参见:doctest.DocTestRunner.__run()
,第 1314-1315 行)。
一个“interactive statement" is a statement list ending with a newline, or a Compound Statement。一个复合语句,例如一个if
或try
语句,"in general, […spans] multiple lines, although in simple incarnations a whole compound statement may be contained in one line."这是一个多行复合语句:
if 1 > 0:
print("As expected")
else:
print("Should not happen")
一个语句列表是一个或多个 simple statement 单行,用分号分隔。
from StringIO import StringIO
s = StringIO(); print("s is created")
因此,问题的 doctest 失败了,因为它包含一个包含三个简单语句的示例,并且没有分号分隔符。将 PS2 字符串更改为 PS1 字符串成功,因为它将文档字符串变成了三个示例的序列,每个示例都有一个简单的语句。尽管这三行一起工作以设置一项功能的一项测试,但它们并不是一个单一的测试夹具。它们是三个测试,其中两个设置状态但不真正测试主要功能。
顺便说一下,您可以通过使用 -v
标志查看 doctest
识别的示例数量。请注意,它说的是“3 tests in __main__.Dummy
”。人们可能认为这三行是一个测试单元,但 doctest
看到了三个示例。前两个示例没有预期的输出。当示例执行并且没有生成输出时,这算作 "pass".
% 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? 在说 "examples can freely use … names defined earlier in the docstring being run."
该部分的前一句 "each time doctest finds a docstring to test, it uses a shallow copy of M’s globals, so that … one test in M can’t leave behind crumbs that accidentally allow another test to work" 有点误导。确实,M 中的一个测试不会影响另一个文档字符串中的测试。但是,在单个文档字符串中,较早的测试肯定会留下碎屑,这很可能会影响后面的测试。
为什么 Python 文档测试库参考 25.2.3.2. How are Docstring Examples Recognized? 中的示例显示了 ...
语法的示例?该示例显示了一个 if
语句,它是多行的复合语句。第二行和后续行用 PS2 字符串标记。