在Python程序中使用doctest和日志记录
#!/usr/bin/python2.4
import logging
import sys
import doctest
def foo(x):
"""
>>> foo (0)
0
"""
print ("%d" %(x))
_logger.debug("%d" %(x))
def _test():
doctest.testmod()
_logger = logging.getLogger()
_logger.setLevel(logging.DEBUG)
_formatter = logging.Formatter('%(message)s')
_handler = logging.StreamHandler(sys.stdout)
_handler.setFormatter(_formatter)
_logger.addHandler(_handler)
_test()
我想在所有的打印语句中使用日志模块。我查了前50个谷歌搜索结果,发现大家都说doctest用的是自己的一份标准输出(stdout)。如果用print语句,它能正常工作;但如果用logger,它会记录到根控制台。有没有人能给我一个可以工作的示例代码,让我能把这两者结合起来?另外,运行nose来测试doctest时,日志输出会在测试结束时追加到最后(前提是你设置了开关),它并不会把这些当作打印语句来处理。
3 个回答
有一种简单且通用的方法是这样的:
import sys
import logging
LOGGER = logging.getLogger(__name__)
if hasattr(sys.modules['__main__'], '_SpoofOut'):
LOGGER.setLevel(logging.DEBUG)
LOGGER.addHandler(logging.StreamHandler())
_SpoofOut
这个属性是由doctest模块添加的。如果这个属性存在,你就可以专门为doctest设置日志记录。例如,在我的例子中,我设置了详细的调试模式,并将日志输出到控制台。
在你的doctest中,在需要记录日志之前,先对你的日志记录器做一个 addHandler(logging.StreamHandler(sys.stdout))
的操作。
举个例子,假设 logger
是你的日志对象:
"""
This is a doctest that will capture output from the logging module.
>>> logger.addHandler(logging.StreamHandler(sys.stdout))
The rest of your doctest, now able to use output from the logging
module...
"""
解释:正如你所说,“doctest使用了它自己的一份stdout”。它通过伪装 sys.stdout
来实现这一点:它把 sys.stdout
设置为 DocTestRunner._fakeout
。一旦doctest开始运行,doctest就会用它的伪装替代真实的 sys.stdout
。如果你在这个时候为sys.stdout创建一个 logging.StreamHandler
,那么sys.stdout将指向doctest的伪装,而不是实际的sys.stdout,这样你就实际上是在为doctest的伪装添加一个StreamHandler,而不是为真实的sys.stdout添加,这样doctest就能看到你记录的输出了。
这个解决方案还有一个好处,就是不依赖于doctest内部的私有变量,比如 _fakeout
或 _SpoofOut
,以防它们将来发生变化。
注意事项:如果你看到
error: [Errno 128] Transport endpoint is not connected
你可能忘记了 import sys
。
我不太明白你为什么想这么做,但如果你真的需要这样做,你可以自己定义一个 DocTestRunner
的子类,并重写它的 run
方法:
#imports left out, code not tested - just to point you in the right direction
class MyDocTestRunner(DocTestRunner):
def run(self, test, compileflags=None, out=None, clear_globs=True):
if out is None:
handler = None
else:
handler = StreamHandler(out)
logger = logging.getLogger() # root logger (say)
if handler:
logger.addHandler(handler)
try:
DocTestRunner.run(self, test, compileflags, out, clear_globs)
finally:
if handler:
logger.removeHandler(handler)
handler.close()
然后用这个新的运行器来替代 DocTestRunner
。