Python 如何确定代码是否在doctest中运行?

Python 如何确定代码是否在doctest中运行?,python,doctest,Python,Doctest,我如何让我的(Python 2.7)代码知道它是否在doctest中运行 场景如下:我有一个函数,print() from __future__ import print_function def printing_func(inarg, file=sys.stdout): # (do some stuff...) print(result, file=file) import sys def in_doctest(): """ Determined by obse

我如何让我的(Python 2.7)代码知道它是否在doctest中运行

场景如下:我有一个函数,
print()

from __future__ import print_function

def printing_func(inarg, file=sys.stdout):
    # (do some stuff...)
    print(result, file=file)
import sys

def in_doctest():
    """
Determined by observation
    """
    if '_pytest.doctest' in sys.modules:
        return True
    ##
    if hasattr(sys.modules['__main__'], '_SpoofOut'):
        return True
    ##
    if sys.modules['__main__'].__dict__.get('__file__', '').endswith('/pytest'):
        return True
    ##
    return False


def test():
    """
    >>> print 'inside comments, running in doctest?', in_doctest()
    inside comments, running in doctest? True
    """
    print 'outside comments, running in doctest?', in_doctest()

if __name__ == '__main__':
    test()
但是当我尝试在doctest中使用
printing_func()
时,测试失败;由于我在调用
print()
时指定了关键字参数
file
,因此输出实际上会转到
sys.stdout
,而不是doctest模块设置的任何默认输出重定向,并且doctest从未看到输出


那么,我如何才能让
printing_func()
知道它是否在doctest内部运行,这样它就知道在调用
print()
时不传递
文件
关键字参数?

我在阅读
doctest.py
后找到了答案;在这里为子孙后代发帖

Doctest通过将新文件描述符分配给
sys.stdout
重定向标准输出。问题是,在doctest重新定义之前,我的函数描述接近原始
sys.stdout
文件描述符的值

相反,如果我执行以下操作:

def printing_func(inarg, file=None):
    # (do some stuff...)

    if file is None:
        file = sys.stdout

    print(result, file=file)
然后
printing_func()
将捕获
sys
模块,而不是
sys.stdout
,当它运行时,如果在测试内部运行,它将从
sys
中检索doctest重新定义的
stdout
属性

编辑:这也提供了一种检查我们是否在doctest中运行的简单方法:

def inside_doctest(original_stdout=sys.stdout):
    return original_stdout != sys.stdout
FWIW(对于迟到和冗余感到抱歉)许多开发人员将“if-test”视为反模式


也就是说,如果测试中的代码在测试时与“真正”运行时做了不同的事情,那就是自找麻烦。即使你相信你这样做是有充分理由的。因此,上面的评论为您的解决方案鼓掌,而您的解决方案没有做到这一点。当我尝试使用“if-test”模式时,我会尝试重构一些东西,这样就不需要了。

Niten版本的
inside\u doctest
似乎太宽泛了。重新定义
sys.stdout
,无论是在日志记录中还是在doctest以外的框架中进行测试时,都不是什么稀奇的事情,因此会产生误报

更窄的测试如下所示:

from __future__ import print_function

def printing_func(inarg, file=sys.stdout):
    # (do some stuff...)
    print(result, file=file)
import sys

def in_doctest():
    """
Determined by observation
    """
    if '_pytest.doctest' in sys.modules:
        return True
    ##
    if hasattr(sys.modules['__main__'], '_SpoofOut'):
        return True
    ##
    if sys.modules['__main__'].__dict__.get('__file__', '').endswith('/pytest'):
        return True
    ##
    return False


def test():
    """
    >>> print 'inside comments, running in doctest?', in_doctest()
    inside comments, running in doctest? True
    """
    print 'outside comments, running in doctest?', in_doctest()

if __name__ == '__main__':
    test()
in\u doctest
测试
\u SpoofOut
类doctest用于替换
sys.stdout
。doctest模块的其他属性也可以通过同样的方式进行验证。并不是说您可以阻止另一个模块重用一个名称,但是这个名称并不常见,所以可能是一个不错的测试

将上述内容放在test.py中。在非doctest模式下运行它,
python test.py

outside comments, running in doctest? False
Trying:
    print 'inside comments, running in doctest?', in_doctest()
Expecting:
    inside comments, running in doctest? True
ok
在doctest详细模式下运行时,
python-m doctest test.py-v
产生:

outside comments, running in doctest? False
Trying:
    print 'inside comments, running in doctest?', in_doctest()
Expecting:
    inside comments, running in doctest? True
ok

我同意其他人的评论,即让代码意识到doctest通常是一个坏主意。我只在有点奇怪的情况下做过这件事——当我需要通过代码生成器创建测试用例时,因为太多了,无法有效地手动创建。但是,如果您需要这样做,以上是一个不错的测试。

我只是检查模块“doctest”是否已加载

def in_doctest():
    import sys
    return 'doctest' in sys.modules

这是一个比试图弄清楚你是否在医生测试中要好得多的计划;向系统中添加代码以检查是否正在测试并使其表现不同的想法对于测试方法来说绝对不是理想的!我大体上同意,但我不认为仅仅为了支持测试基础设施而这样做是有害的。@Niten版本的
inside\u doctest
有点太广泛了——对我来说,误报的可能性太高了。请参阅我的答案以获得更严格的解决方案。