outputtrap.py
258 lines
| 7.7 KiB
| text/x-python
|
PythonLexer
fperez
|
r0 | # -*- coding: utf-8 -*- | ||
"""Class to trap stdout and stderr and log them separately. | ||||
Fernando Perez
|
r1853 | """ | ||
fperez
|
r0 | |||
#***************************************************************************** | ||||
# Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#***************************************************************************** | ||||
fperez
|
r52 | import exceptions | ||
import sys | ||||
fperez
|
r0 | from cStringIO import StringIO | ||
class OutputTrapError(exceptions.Exception): | ||||
"""Exception for OutputTrap class.""" | ||||
def __init__(self,args=None): | ||||
exceptions.Exception.__init__(self) | ||||
self.args = args | ||||
class OutputTrap: | ||||
"""Class to trap standard output and standard error. They get logged in | ||||
StringIO objects which are available as <instance>.out and | ||||
<instance>.err. The class also offers summary methods which format this | ||||
data a bit. | ||||
A word of caution: because it blocks messages, using this class can make | ||||
debugging very tricky. If you are having bizarre problems silently, try | ||||
turning your output traps off for a while. You can call the constructor | ||||
with the parameter debug=1 for these cases. This turns actual trapping | ||||
off, but you can keep the rest of your code unchanged (this has already | ||||
been a life saver). | ||||
Example: | ||||
# config: trapper with a line of dots as log separator (final '\\n' needed) | ||||
config = OutputTrap('Config','Out ','Err ','.'*80+'\\n') | ||||
# start trapping output | ||||
config.trap_all() | ||||
# now all output is logged ... | ||||
# do stuff... | ||||
# output back to normal: | ||||
config.release_all() | ||||
# print all that got logged: | ||||
print config.summary() | ||||
# print individual raw data: | ||||
print config.out.getvalue() | ||||
print config.err.getvalue() | ||||
""" | ||||
def __init__(self,name='Generic Output Trap', | ||||
out_head='Standard Output. ',err_head='Standard Error. ', | ||||
sum_sep='\n',debug=0,trap_out=0,trap_err=0, | ||||
quiet_out=0,quiet_err=0): | ||||
self.name = name | ||||
self.out_head = out_head | ||||
self.err_head = err_head | ||||
self.sum_sep = sum_sep | ||||
self.out = StringIO() | ||||
self.err = StringIO() | ||||
self.out_save = None | ||||
self.err_save = None | ||||
self.debug = debug | ||||
self.quiet_out = quiet_out | ||||
self.quiet_err = quiet_err | ||||
if trap_out: | ||||
self.trap_out() | ||||
if trap_err: | ||||
self.trap_err() | ||||
def trap_out(self): | ||||
"""Trap and log stdout.""" | ||||
if sys.stdout is self.out: | ||||
raise OutputTrapError,'You are already trapping stdout.' | ||||
if not self.debug: | ||||
self._out_save = sys.stdout | ||||
sys.stdout = self.out | ||||
def release_out(self): | ||||
"""Release stdout.""" | ||||
if not self.debug: | ||||
if not sys.stdout is self.out: | ||||
raise OutputTrapError,'You are not trapping stdout.' | ||||
sys.stdout = self._out_save | ||||
self.out_save = None | ||||
def summary_out(self): | ||||
"""Return as a string the log from stdout.""" | ||||
out = self.out.getvalue() | ||||
if out: | ||||
if self.quiet_out: | ||||
return out | ||||
else: | ||||
return self.out_head + 'Log by '+ self.name + ':\n' + out | ||||
else: | ||||
return '' | ||||
def flush_out(self): | ||||
"""Flush the stdout log. All data held in the log is lost.""" | ||||
self.out.close() | ||||
self.out = StringIO() | ||||
def trap_err(self): | ||||
"""Trap and log stderr.""" | ||||
if sys.stderr is self.err: | ||||
raise OutputTrapError,'You are already trapping stderr.' | ||||
if not self.debug: | ||||
self._err_save = sys.stderr | ||||
sys.stderr = self.err | ||||
def release_err(self): | ||||
"""Release stderr.""" | ||||
if not self.debug: | ||||
if not sys.stderr is self.err: | ||||
raise OutputTrapError,'You are not trapping stderr.' | ||||
sys.stderr = self._err_save | ||||
self.err_save = None | ||||
def summary_err(self): | ||||
"""Return as a string the log from stderr.""" | ||||
err = self.err.getvalue() | ||||
if err: | ||||
if self.quiet_err: | ||||
return err | ||||
else: | ||||
return self.err_head + 'Log by '+ self.name + ':\n' + err | ||||
else: | ||||
return '' | ||||
def flush_err(self): | ||||
"""Flush the stdout log. All data held in the log is lost.""" | ||||
self.err.close() | ||||
self.err = StringIO() | ||||
def trap_all(self): | ||||
"""Trap and log both stdout and stderr. | ||||
Cacthes and discards OutputTrapError exceptions raised.""" | ||||
try: | ||||
self.trap_out() | ||||
except OutputTrapError: | ||||
pass | ||||
try: | ||||
self.trap_err() | ||||
except OutputTrapError: | ||||
pass | ||||
def release_all(self): | ||||
"""Release both stdout and stderr. | ||||
Cacthes and discards OutputTrapError exceptions raised.""" | ||||
try: | ||||
self.release_out() | ||||
except OutputTrapError: | ||||
pass | ||||
try: | ||||
self.release_err() | ||||
except OutputTrapError: | ||||
pass | ||||
def summary_all(self): | ||||
"""Return as a string the log from stdout and stderr, prepending a separator | ||||
to each (defined in __init__ as sum_sep).""" | ||||
sum = '' | ||||
sout = self.summary_out() | ||||
if sout: | ||||
sum += self.sum_sep + sout | ||||
serr = self.summary_err() | ||||
if serr: | ||||
sum += '\n'+self.sum_sep + serr | ||||
return sum | ||||
def flush_all(self): | ||||
"""Flush stdout and stderr""" | ||||
self.flush_out() | ||||
self.flush_err() | ||||
# a few shorthands | ||||
trap = trap_all | ||||
release = release_all | ||||
summary = summary_all | ||||
flush = flush_all | ||||
# end OutputTrap | ||||
#**************************************************************************** | ||||
# Module testing. Incomplete, I'm lazy... | ||||
def _test_all(): | ||||
"""Module testing functions, activated when the module is called as a | ||||
script (not imported).""" | ||||
# Put tests for this module in here. | ||||
# Define them as nested functions so they don't clobber the | ||||
# pydoc-generated docs | ||||
def _test_(): | ||||
name = '' | ||||
print '#'*50+'\nRunning test for ' + name | ||||
# ... | ||||
print 'Finished test for '+ name +'\n'+'#'*50 | ||||
def _test_OutputTrap(): | ||||
trap = OutputTrap(name = 'Test Trap', sum_sep = '.'*50+'\n', | ||||
out_head = 'SOut. ', err_head = 'SErr. ') | ||||
name = 'OutputTrap class' | ||||
print '#'*50+'\nRunning test for ' + name | ||||
print 'Trapping out' | ||||
trap.trap_out() | ||||
print >>sys.stdout, '>>stdout. stdout is trapped.' | ||||
print >>sys.stderr, '>>stderr. stdout is trapped.' | ||||
trap.release_out() | ||||
print trap.summary_out() | ||||
print 'Trapping err' | ||||
trap.trap_err() | ||||
print >>sys.stdout, '>>stdout. stderr is trapped.' | ||||
print >>sys.stderr, '>>stderr. stderr is trapped.' | ||||
trap.release_err() | ||||
print trap.summary_err() | ||||
print 'Trapping all (no flushing)' | ||||
trap.trap_all() | ||||
print >>sys.stdout, '>>stdout. stdout/err is trapped.' | ||||
print >>sys.stderr, '>>stderr. stdout/err is trapped.' | ||||
trap.release_all() | ||||
print trap.summary_all() | ||||
print 'Trapping all (flushing first)' | ||||
trap.flush() | ||||
trap.trap_all() | ||||
print >>sys.stdout, '>>stdout. stdout/err is trapped.' | ||||
print >>sys.stderr, '>>stderr. stdout/err is trapped.' | ||||
trap.release_all() | ||||
print trap.summary_all() | ||||
print 'Finished test for '+ name +'\n'+'#'*50 | ||||
# call the actual tests here: | ||||
_test_OutputTrap() | ||||
if __name__=="__main__": | ||||
# _test_all() # XXX BROKEN. | ||||
pass | ||||
#************************ end of file <OutputTrap.py> ************************ | ||||