Source code for schrodinger.ui.qt.appframework2.debugcore

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')