【问题标题】:Doctests: How to suppress/ignore output?Doctests:如何抑制/忽略输出?
【发布时间】:2010-10-05 09:12:27
【问题描述】:

以下(废话)Python 模块的 doctest 失败:

"""
>>> L = []
>>> if True:
...    append_to(L) # XXX
>>> L
[1]
"""

def append_to(L):
    L.append(1)
    class A(object):
        pass
    return A()

import doctest; doctest.testmod()

这是因为标记为 XXX 的行后面的输出是<__main__.A object at ...>(由append_to 返回)。当然,我可以将这个输出直接放在标记为 XXX 的行之后,但在我的情况下,这会分散读者对实际测试内容的注意力,即函数append_to 的副作用。那么如何抑制该输出或如何忽略它。我试过了:

"""
>>> L = []
>>> if True:
...    append_to(L) # doctest: +ELLIPSIS
    ...
>>> L
[1]
"""

def append_to(L):
    L.append(1)
    class A(object):
        pass
    return A()

import doctest; doctest.testmod()

但是,这会产生ValueError: line 4 of the docstring for __main__ has inconsistent leading whitespace: ' ...'

我不想做的是将行 append_to(L) 更改为 _ = append_to(L) 之类的东西,这会抑制输出,因为 doctest 用于文档目的并向读者展示模块应该是什么用过的。 (在记录的情况下,append_to 应该像语句一样使用,而不是像函数一样。编写 _ = append_to(L) 会使读者偏离这一点。)

【问题讨论】:

    标签: python doctest


    【解决方案1】:

    rewrite: 现在确实可以了;我意识到我之前写的“doctest”实际上没有被解析为模块文档字符串,所以测试没有通过:它只是没有运行。

    我确保仔细检查了这个。

    __doc__ = """
    >>> L = []
    >>> if True:
    ...    append_to(L) # doctest: +IGNORE_RESULT
    >>> L
    [1]
    """.replace('+IGNORE_RESULT', '+ELLIPSIS\n<...>')
    
    def append_to(L):
        L.append(1)
        class A(object):
            pass
        return A()
    

    我不确定这是否更具可读性。请注意,&lt;...&gt; 没有什么特别之处:它只有在实际返回值具有这种形式时才有效,就像在这种情况下一样(即它是 &lt;module.A object at 0x...&gt;)。 ELLIPSIS 选项使...“匹配实际输出中的任何子字符串¹。所以我认为没有办法让它匹配整个输出。

    更新:要以“正确”方式执行此操作,您似乎需要调用doctest.register_optionflag('IGNORE_RESULT'),子类doctest.OptionChecker,并安排使用该子类的一个实例通过 doctest。大概这意味着不能通过$ python -m doctest your_module.py 运行您的文档测试。

    【讨论】:

    • 如果用 register_optionflag 的“正确”方式得到答案,OutputChecker 的实现子类以及如何运行测试,那就太好了。或者至少有一个链接到一个做类似事情的例子。
    【解决方案2】:

    我最终遇到了这个问题,因为我需要 +IGNORE_RESULT 行为,但需要在 sphinx 文档上运行 doc 测试。 @intuited 发布的 replace 答案在这种情况下不起作用,并且没有提到的“正确”解决方案的代码。因此,我发布了我最终得到的结果。

    作为问题的直接答案,解决方案是:

    __doc__ = """
    >>> L = []
    >>> if True:
    ...    append_to(L) # doctest: +IGNORE_RESULT
    >>> L
    [1]
    """
    
    def append_to(L):
        L.append(1)
        class A(object):
            pass
        return A()
    
    if __name__ == "__main__":
        import doctest
    
        IGNORE_RESULT = doctest.register_optionflag('IGNORE_RESULT')
    
        OutputChecker = doctest.OutputChecker
        class CustomOutputChecker(OutputChecker):
            def check_output(self, want, got, optionflags):
                if IGNORE_RESULT & optionflags:
                    return True
                return OutputChecker.check_output(self, want, got, optionflags)
    
        doctest.OutputChecker = CustomOutputChecker
        doctest.testmod()
    

    为了我对 doc 测试 sphinx 文档的特殊需要,请添加到 conf.py 文件:

    import doctest
    
    IGNORE_RESULT = doctest.register_optionflag('IGNORE_RESULT')
    
    OutputChecker = doctest.OutputChecker
    class CustomOutputChecker(OutputChecker):
        def check_output(self, want, got, optionflags):
            if IGNORE_RESULT & optionflags:
                return True
            return OutputChecker.check_output(self, want, got, optionflags)
    
    doctest.OutputChecker = CustomOutputChecker
    

    然后使用如下命令进行测试:

    sphinx-build -M doctest source_dir build_dir source_dir/file.rst
    

    【讨论】:

      【解决方案3】:

      请尽量给出完全独立、可运行的代码;即使在演示问题时,代码也应自行运行以重现问题,因此解决方案可以直接复制代码以演示答案。

      我不知道有什么干净的解决方案,我以前也遇到过;这似乎是 doctests 提供的模糊(更直白地说:草率)测试定义的副作用。一种解决方法是记住您可以在 doctests 中定义函数,因此您可以将整个测试包含为单个函数而不是单独的语句。

      def append_to(l):
          """
          >>> L = []
          >>> def test():
          ...     if True:
          ...         append_to(L) # XXX
          >>> test()
          >>> L
          [1]
      
          >>> def test():
          ...     L = []
          ...     if True:
          ...         append_to(L) # XXX
          ...     return L
          >>> test()
          [1]
      
          """
          l.append(1)
          return object()
      
      if __name__ == "__main__":
          import doctest
          doctest.testmod()
      

      【讨论】:

      • 给出独立的、可运行的代码是什么意思?我给出的代码可以直接用 Python 解释器运行(产生我提到的失败)——至少这在这里使用 Python 2.6 有效。至于您的回答:我还考虑过将所有内容包装在一个函数中,以便返回值为None,但正如您所写的那样,这看起来不是一个非常干净的解决方案。
      • 我只是抄错了; doctest 被写成一个单独的字符串,而不是被测试的属性——我从不这样做。无论如何,只要您的测试确实解决了一个结果,上述内容就相当干净了。如果测试不符合该模式,例如。如果你有多个相互关联的语句,测试一些结果值而不是其他的,那么它就不会很好地工作。确实需要一个 doctest.DISCARD 标志。
      猜你喜欢
      • 2012-08-06
      • 2022-10-18
      • 1970-01-01
      • 1970-01-01
      • 2013-04-25
      • 1970-01-01
      • 2013-02-18
      • 2014-10-17
      • 2018-08-14
      相关资源
      最近更新 更多