|
|
# -*- coding: utf-8 -*-
|
|
|
"""Class to trap stdout and stderr and log them separately.
|
|
|
"""
|
|
|
|
|
|
#*****************************************************************************
|
|
|
# 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.
|
|
|
#*****************************************************************************
|
|
|
|
|
|
import exceptions
|
|
|
import sys
|
|
|
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> ************************
|
|
|
|