import datetime
import inspect
import sys
PRINT_TAG = 'Print'
SIGNALS_TAG = 'Signals'
FUNCTIONS_TAG = 'Functions'
DEFAULT_TAG = 'Log'
#===============================================================================
# Main functions
#===============================================================================
global_original_stdout = sys.stdout
global_original_stderr = sys.stderr
debug_viewer = None
[docs]def start(viewer_class=None, redirect=True):
if viewer_class is None:
viewer_class = StdoutDebugViewer
global debug_viewer
debug_viewer = viewer_class.viewer()
DebugStdout.redirect = redirect
sys.stdout = DebugStdout
DebugStderr.redirect = redirect
sys.stderr = DebugStderr
[docs]def stop():
global debug_viewer
sys.stdout = global_original_stdout
debug_viewer = None
[docs]def log(text, tag=DEFAULT_TAG):
if debug_viewer:
debug_viewer.log(text, tag)
#===============================================================================
# Stdout/stderr redirectors
#===============================================================================
[docs]class DebugStdout(object):
redirect = True
[docs] @classmethod
def write(cls, message):
if not debug_viewer:
return
if not cls.redirect:
global_original_stdout.write(message)
debug_viewer.debugPrint(message)
[docs] @classmethod
def flush(cls):
pass
[docs]class DebugStderr(object):
redirect = True
[docs] @classmethod
def write(cls, message):
if not debug_viewer:
return
if not cls.redirect:
global_original_stderr.write(message)
debug_viewer.debugPrint(message)
[docs] @classmethod
def flush(cls):
pass
#===============================================================================
# Decorators
#===============================================================================
[docs]def debug_func(func):
def debug_decorate(*args, **kwargs):
if debug_viewer:
debug_viewer.log('Enter: ' + func.__name__, FUNCTIONS_TAG)
try:
return func(*args, **kwargs)
finally:
if debug_viewer:
debug_viewer.log('Exit: ' + func.__name__, FUNCTIONS_TAG)
return debug_decorate
[docs]def debug_func_args(func):
def debug_decorate(*args, **kwargs):
arglist = list(map(str, args))
for kw, val in kwargs.items():
arglist.append('%s=%s' % (str(kw), str(val)))
funcstring = func.__name__ + '(' + ', '.join(arglist) + ')'
if debug_viewer:
debug_viewer.log('Enter: ' + funcstring, FUNCTIONS_TAG)
try:
retval = 'Exception'
retval = func(*args, **kwargs)
return retval
finally:
if debug_viewer:
debug_viewer.log(
'Exit: %s. Returned: %s' % (func.__name__, str(retval)),
FUNCTIONS_TAG)
return debug_decorate
#===============================================================================
# General debug clases
#===============================================================================
[docs]class LogEntry(object):
[docs] def __init__(self, text, tag=None, timestamp=None, caller=None, indent=0):
self.text = text
self.tag = tag
self.timestamp = timestamp
self.caller = caller
self.indent = indent
#===============================================================================
# Debug Viewers
#===============================================================================
[docs]class AbstractDebugViewer():
_singleton = None
skip_funcnames = ['debug_decorate']
recur_caller = True
fast = True
running_message = ''
[docs] @classmethod
def viewer(cls):
if cls._singleton is None or not isinstance(cls._singleton, cls):
# The isinstance check covers cases of panel inheritance
cls._singleton = cls()
return cls._singleton
[docs] def debugPrint(self, message):
self.running_message += message
if message.endswith('\n'):
entry = self.makeLogEntry(self.running_message[:-1],
tag=PRINT_TAG,
offset=3,
inset=1)
self.running_message = ''
self.writeLogEntry(entry)
[docs] def log(self, text, tag, offset=3, inset=1):
entry = self.makeLogEntry(text, tag, offset=offset, inset=inset)
self.writeLogEntry(entry)
[docs] def writeLogEntry(self, entry):
raise NotImplementedError('writeLogEngry must be implemented in '
'DebugViewer class %s' %
self.__class__.__name__)
[docs] def makeLogEntry(self, text, tag, offset=3, inset=1):
timestamp = str(datetime.datetime.now().time())
indent = self.getAutoIndent(offset=offset, inset=inset)
caller = self.getAutoCallerString(offset=offset, inset=inset)
entry = LogEntry(text,
tag=tag,
timestamp=timestamp,
caller=caller,
indent=indent)
return entry
[docs] def getAutoIndent(self, max_indent=8, offset=3, inset=1):
if self.fast:
return 0
depth = 0
real_depth = 1
while depth < max_indent:
try:
funcname = inspect.stack()[real_depth + offset][3]
if funcname not in self.skip_funcnames:
depth += 1
real_depth += 1
except IndexError:
break
depth = depth - inset
return depth
[docs] def getAutoCallerString(self, max_depth=8, offset=3, inset=1):
if self.fast:
return ''
depth = 0
real_depth = 1
funcs = []
if not self.recur_caller:
max_depth = 1
while depth < max_depth:
try:
funcname = inspect.stack()[real_depth + offset][3]
if funcname not in self.skip_funcnames:
depth += 1
funcs.append(funcname)
real_depth += 1
except IndexError:
break
funcs.reverse()
funcs = funcs[inset:]
header = '->'.join(funcs)
return header
[docs] def logString(self, entry):
add_caller = False
if add_caller:
ls = '%s%12s %s\n %s%s' % (entry.timestamp, entry.tag,
entry.caller, ' ' *
(entry.indent), entry.text)
else:
ls = '%s%12s %s%s' % (entry.timestamp, entry.tag, ' ' *
(entry.indent), entry.text)
return ls
[docs]class StdoutDebugViewer(AbstractDebugViewer):
[docs] def __init__(self):
self.original_stdout = global_original_stdout
[docs] def writeLogEntry(self, entry):
logstring = self.logString(entry)
global_original_stdout.write(logstring)
global_original_stdout.write('\n')
[docs]class FileDebugViewer(AbstractDebugViewer):
[docs] def __init__(self, filename=None):
if filename is None:
filename = 'debug.txt'
self.filename = filename
with open(self.filename, 'wb'):
pass
[docs] def writeLogEntry(self, entry):
logstring = self.logString(entry)
with open(self.filename, 'ab') as f:
f.write(logstring)
f.write('\n')