python testsuite不会在控制台上显示输出

时间:2016-07-05 06:58:49

标签: python stdout stderr python-unittest

这是我的示例代码

class StreamToLogger(object):
    def __init__(self, logger, log_level=logging.INFO):
        self.logger = logger
        self.log_level = log_level
        self.linebuf = ''

    def write(self, buf):
        for line in buf.rstrip().splitlines():
            self.logger.log(self.log_level, line.rstrip())



logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s:%(levelname)s:%(name)s:%(message)s',
                    filename="logger.log",
                    filemode='w'
                    )

stdout_logger = logging.getLogger('STDOUT')
sl = StreamToLogger(stdout_logger, logging.INFO)
sys.stdout = sl

stderr_logger = logging.getLogger('STDERR')
sl = StreamToLogger(stderr_logger, logging.ERROR)
sys.stderr = sl

suite = suite()
tests = unittest.TextTestRunner(descriptions=True, verbosity=2).run(suite)

默认情况下,TextTestRunner将流用作stderr。我已经使用stderr在日志文件中写入输出并且它正在工作。我们可以在同一个脚本中多次使用stderr吗?

2 个答案:

答案 0 :(得分:1)

我对您的代码进行了新的分析。那里的问题是

  1. 为stdout和stderr定义两个流。 TextTextRunner只使用一个流:默认情况下为sys.stderr。
  2. TextTestRunner默认使用sys.stderr,这不是你的sys.stderr。这看起来很奇怪,稍后再说。所以你的代码就像没有定义任何其他输出流一样工作。
  3. 当您调用TextTextRunner()而不是默认值时,情况会发生变化,但是如果您通过显式重新定义了sys.stderr。现在输出将被重定向到您的文件:

    # does not at all what you want (writes output to stderr):
    tests = unittest.TextTestRunner( descriptions=True, verbosity=2 ).run(suite)
    # does not everything what you want (writes output to file only):
    tests = unittest.TextTestRunner\
                               ( stream=sys.stderr, descriptions=True, verbosity=2 ).run(suite) 
    

    满足一个要求(写入文件)但不满足另一个要求。这是因为只使用了一个已定义的流(通过流传递给TextTestRunner的流)。对于第二个要求(也要写入控制台),必须有一种方法可以将一个流(重定向的stderr)写入两个输出设备。我认为只有以适当的方式更改StreamToLogger.write()方法才能实现这一点:

    def write( self, buf ) :
        my_console_stream_instance.write(buf)
        my_file_stream_instance.write(buf)
    

    其中my_console_stream_instance是写入控制台的流,my_file_stream_instance是写入文件的流。所以请看下面的例子,它应该做你想要的:

    class StreamToLogger( object ):
        def __init__( self ):
            self.terminal = sys.stderr
            self.log = open( "logger.log", "w" )
    
        def write( self, buf ):
            self.terminal.write(buf) # write to stderr
            self.log.write(buf) # write to file
    
    sys.stderr = StreamToLogger()
    
    suite = suite()
    tests = unittest.TextTestRunner\
                             ( stream=sys.stderr, descriptions=True, verbosity=2 ).run( suite )
    

    现在确实有两个输出:一个到屏幕,一个到文件。

    但是为什么需要将sys.stderr传递给TextTestRunner的stream参数?乍一看似乎是一样的。但是在章节function definitions中的Python语言参考中是解释:

      

    当函数定义为时,将计算默认参数值   执行。这意味着表达式被评估一次,当时   定义函数,并使用相同的“预先计算”值   每次通话。这对了解何时来说尤为重要   default参数是一个可变对象。

    在执行stderr重定义之前,将评估TextTestRunner中stream参数的默认sys.stderr。这就是为什么我们必须通过我们重新命名的sys.stderr。一个小小的证据是:在导入unittest之前重新定义sys.stderr :代码运行良好而不传递重新定义的sys.stderr。

答案 1 :(得分:0)

对我来说,这不是很清楚,你想做什么。将日志记录模块的所有控制台输出写入文件和控制台?如果是这样的话:

# Create a logging object
logger = logging.getLogger ( 'STDOUT+FILE' )
logger.setLevel ( logging.DEBUG )

# Create a file handler and set level to INFO (or other value)
file_ch = logging.FileHandler( 'logger.log', 'w' )
file_ch.setLevel ( logging.DEBUG )
# ... and add it to the logging object
logger.addHandler( file_ch )

# Create a stream  handler and set level to INFO
console_ch = logging.StreamHandler( )
console_ch.setLevel ( logging.DEBUG )
# ... and add it to the logging object
logger.addHandler( console_ch )

# Do some logging
logger.debug( 'Logging Test' )
logger.debug( '------------' )
logger.info( 'this is an info log' )
logger.warn( 'this is a warn log' )
logger.debug( 'this is a debug log' )
logger.error( 'this is an error log' )
logger.critical( 'this is a critical log' )

myLogFile.log 的内容现在是

Logging Test
------------ 
this is an info log 
this is a warn log 
this is a debug log 
this is a critical log

控制台上显示相同的文字

更多信息here