aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'debug.py')
-rw-r--r--debug.py501
1 files changed, 501 insertions, 0 deletions
diff --git a/debug.py b/debug.py
new file mode 100644
index 0000000..925f49c
--- /dev/null
+++ b/debug.py
@@ -0,0 +1,501 @@
+#################################################################################
+# LAYMAN - DEBUGGING FUNCTIONS
+#################################################################################
+# debug.py -- Utility function for debugging
+# Copyright 2005 - 2006 Gunnar Wrobel
+# Distributed under the terms of the GNU General Public License v2
+
+__version__ = "$Id: debug.py 153 2006-06-05 06:03:16Z wrobel $"
+
+#################################################################################
+##
+## Dependancies
+##
+#################################################################################
+
+import sys, inspect
+
+from optparse import OptionGroup
+
+#################################################################################
+##
+## Color codes (taken from portage)
+##
+#################################################################################
+
+esc_seq = '\x1b['
+
+codes = {}
+codes['reset'] = esc_seq + '39;49;00m'
+codes['red'] = esc_seq + '31;01m'
+codes['green'] = esc_seq + '32;01m'
+codes['yellow'] = esc_seq + '33;01m'
+codes['turquoise'] = esc_seq + '36;01m'
+
+#################################################################################
+##
+## Message Class
+##
+#################################################################################
+
+class Message:
+ #FIXME: Think about some simple doctests before you modify this class the
+ # next time.
+
+ def __init__(self, module = '',
+ err = sys.stderr,
+ dbg = sys.stderr,
+ debugging_level = 4,
+ debugging_verbosity = 2,
+ info_level = 4,
+ warn_level = 4,
+ col = True,
+ mth = ['*'],
+ obj = ['*'],
+ var = ['*']):
+
+ # A description of the module that is being debugged
+ self.debug_env = module
+
+ # Where should the debugging output go? This can also be a file
+ self.debug_out = dbg
+
+ # Where should the error output go? This can also be a file
+ self.error_out = err
+
+ # The higher the level the more information you will get
+ self.warn_lev = warn_level
+
+ # The higher the level the more information you will get
+ self.info_lev = info_level
+
+ # The highest level of debugging messages acceptable for output
+ # The higher the level the more output you will get
+ self.debug_lev = debugging_level
+
+ # The debugging output can range from very verbose (3) to
+ # very compressed (1)
+ self.debug_vrb = debugging_verbosity
+
+ # Which methods should actually be debugged?
+ # Use '*' to indicate 'All methods'
+ self.debug_mth = mth
+
+ # Which objects should actually be debugged?
+ # Use '*' to indicate 'All objects'
+ self.debug_obj = obj
+
+ # Which variables should actually be debugged?
+ # Use '*' to indicate 'All variables'
+ self.debug_var = var
+
+ # Exclude class variables by default
+ self.show_class_variables = False
+
+ # Should the output be colored?
+ self.use_color = col
+
+ self.has_error = False
+
+
+ ############################################################################
+ # Add command line options
+
+ def cli_opts(self, parser):
+
+ group = OptionGroup(parser,
+ '<Debugging options>',
+ 'Control the debugging features of '
+ + self.debug_env)
+
+ group.add_option('--debug',
+ action = 'store_true',
+ help = 'Activates debugging features.')
+
+ group.add_option('--debug-level',
+ action = 'store',
+ type = 'int',
+ help = 'A value between 0 and 10. 0 means no debugging '
+ 'messages will be selected, 10 selects all debugging me'
+ 'ssages. Default is "4".')
+
+ group.add_option('--debug-verbose',
+ action = 'store',
+ type = 'int',
+ help = 'A value between 1 and 3. Lower values yield les'
+ 's verbose debugging output. Default is "2".')
+
+ group.add_option('--debug-methods',
+ action = 'store',
+ help = 'Limits the methods that will return debugging o'
+ 'utput. The function name is sufficient and there is no'
+ 'difference between class methods or general functions.'
+ ' Several methods can be specified by seperating them w'
+ ' with a comma. Default is "*" which specifies all meth'
+ 'ods.')
+
+ group.add_option('--debug-classes',
+ action = 'store',
+ help = 'Limits the classes that will return debugging o'
+ 'utput. Specify only the class name not including the m'
+ 'odules in which the class is defined (e.g. MyModule.ma'
+ 'in.Main should only be represented by "Main"). Several'
+ 'classes can be specified by seperating them with a com'
+ 'ma. Default is "*" which specifies all classes.')
+
+ group.add_option('--debug-variables',
+ action = 'store',
+ help = 'Limits the variables that will return debugging'
+ ' output. Several variables can be specified by seperat'
+ 'ing them with a comma. Default is "*" which specifies '
+ 'all variables.')
+
+ group.add_option('--debug-class-vars',
+ action = 'store_true',
+ help = 'In default mode the debugging code will only re'
+ 'turn information on the local variable which does not '
+ 'include the class variables. Use this switch to add al'
+ 'l values that are provided by "self".')
+
+ group.add_option('--debug-nocolor',
+ action = 'store_true',
+ help = 'Deactivates colors in the debugging output.')
+
+ parser.add_option_group(group)
+
+
+ #############################################################################
+ # Handle command line options
+
+ def cli_handle(self, options):
+
+ if (options.__dict__.has_key('debug')
+ and options.__dict__['debug']):
+ self.debug_on()
+ else:
+ self.debug_off()
+ return
+
+ if (options.__dict__.has_key('debug_class_vars')
+ and options.__dict__['debug_class_vars']):
+ self.class_variables_on()
+ else:
+ self.class_variables_off()
+
+ if (options.__dict__.has_key('debug_nocolor')
+ and options.__dict__['debug_nocolor']):
+ self.color_off()
+ else:
+ self.color_on()
+
+ if (options.__dict__.has_key('debug_level') and
+ options.__dict__['debug_level']):
+ dbglvl = int(options.__dict__['debug_level'])
+ if dbglvl < 0:
+ dbglvl = 0
+ if dbglvl > 10:
+ dbglvl = 10
+ self.set_debug_level(dbglvl)
+
+ if (options.__dict__.has_key('debug_verbose') and
+ options.__dict__['debug_verbose']):
+ dbgvrb = int(options.__dict__['debug_verbose'])
+ if dbgvrb < 1:
+ dbgvrb = 1
+ if dbgvrb > 3:
+ dbgvrb = 3
+ self.set_debug_verbosity(dbgvrb)
+
+ for i in [('debug_methods', self.set_debug_methods),
+ ('debug_classes', self.set_debug_classes),
+ ('debug_variables', self.set_debug_variables),]:
+
+ if (options.__dict__.has_key(i[0]) and
+ options.__dict__[i[0]]):
+ i[1](options.__dict__[i[0]])
+
+
+ #############################################################################
+ ## Helper Functions
+
+ def set_module(self, module):
+
+ self.debug_env = module
+
+ def set_debug_methods(self, methods):
+
+ methods = methods.split(',')
+
+ if methods:
+ self.debug_mth = methods
+
+ def set_debug_classes(self, classes):
+
+ classes = classes.split(',')
+
+ if classes:
+ self.debug_obj = classes
+
+ def set_debug_variables(self, variables):
+
+ variables = variables.split(',')
+
+ if variables:
+ self.debug_var = variables
+
+ def maybe_color (self, col, text):
+ if self.use_color:
+ return codes[col] + text + codes['reset']
+ return text
+
+ def set_info_level(self, info_level = 4):
+ self.info_lev = info_level
+
+ def info_off(self):
+ self.set_info_level(0)
+
+ def info_on(self, info_level = 4):
+ self.set_info_level(info_level)
+
+ def set_warn_level(self, warn_level = 4):
+ self.warn_lev = warn_level
+
+ def warn_off(self):
+ self.set_warn_level(0)
+
+ def warn_on(self, warn_level = 4):
+ self.set_warn_level(warn_level)
+
+ def set_debug_level(self, debugging_level = 4):
+ self.debug_lev = debugging_level
+
+ def set_debug_verbosity(self, debugging_verbosity = 2):
+ self.debug_vrb = debugging_verbosity
+
+ def debug_off(self):
+ self.set_debug_level(0)
+
+ def debug_on(self):
+ self.set_debug_level()
+
+ def color_off(self):
+ self.use_color = False
+
+ def color_on(self):
+ self.use_color = True
+
+ def class_variables_off(self):
+ self.show_class_variables = False
+
+ def class_variables_on(self):
+ self.show_class_variables = True
+
+ #############################################################################
+ ## Output Functions
+
+ def notice (self, note):
+ print note
+
+ def info (self, info, level = 4):
+
+ info = str(info)
+
+ if level > self.info_lev:
+ return
+
+ for i in info.split('\n'):
+ print self.maybe_color('green', '* ') + i
+
+ def status (self, message, status, info = 'ignored'):
+
+ message = str(message)
+
+ lines = message.split('\n')
+
+ if not lines:
+ return
+
+ for i in lines[0:-1]:
+ print self.maybe_color('green', '* ') + i
+
+ i = lines[-1]
+
+ if len(i) > 58:
+ i = i[0:57]
+
+ if status == 1:
+ result = '[' + self.maybe_color('green', 'ok') + ']'
+ elif status == 0:
+ result = '[' + self.maybe_color('red', 'failed') + ']'
+ else:
+ result = '[' + self.maybe_color('yellow', info) + ']'
+
+ print self.maybe_color('green', '* ') + i + ' ' + '.' * (58 - len(i)) \
+ + ' ' + result
+
+ def warn (self, warn, level = 4):
+
+ warn = str(warn)
+
+ if level > self.warn_lev:
+ return
+
+ for i in warn.split('\n'):
+ print self.maybe_color('yellow', '* ') + i
+
+ def error (self, error):
+
+ error = str(error)
+
+ for i in error.split('\n'):
+ print >> self.error_out, self.maybe_color('red', '* ') + i
+ self.has_error = True
+
+ def die (self, error):
+
+ error = str(error)
+
+ for i in error.split('\n'):
+ self.error(self.maybe_color('red', 'Fatal error: ') + i)
+ self.error(self.maybe_color('red', 'Fatal error(s) - aborting'))
+ sys.exit(1)
+
+ def debug (self, message, level = 4):
+ '''
+ This is a generic debugging method.
+ '''
+ ## Check the debug level first. This is the most inexpensive check.
+ if level > self.debug_lev:
+ return
+
+ ## Maybe this should be debugged. So get the stack first.
+ stack = inspect.stack()
+
+ ## This can probably never happen but does not harm to check
+ ## that there is actually something calling this function
+ if len(stack) < 2:
+ return
+
+ ## Get the stack length to determine indentation of the debugging output
+ stacklength = len(stack)
+ ls = ' ' * stacklength
+
+ ## Get the information about the caller
+ caller = stack[1]
+
+ ## The function name of the calling frame is the fourth item in the list
+ callermethod = caller[3]
+
+ ## Is this actually one of the methods that should be debugged?
+ if not '*' in self.debug_mth and not callermethod in self.debug_mth:
+ return
+
+ ## Still looks like this should be debugged. So retrieve the dictionary
+ ## of local variables from the caller
+ callerlocals = inspect.getargvalues(caller[0])[3]
+
+ ## Is the caller an obejct? If so he provides 'self'
+ if 'self' in callerlocals.keys():
+ callerobject = callerlocals['self']
+ del callerlocals['self']
+ if self.show_class_variables:
+ cv = inspect.getmembers(callerobject,
+ lambda x: not inspect.ismethod(x))
+ callerlocals.sync(cv)
+ else:
+ callerobject = None
+
+ # Remove variables not requested
+ if not '*' in self.debug_var:
+ callerlocals = dict([i for i in callerlocals.items()
+ if i[0] in self.debug_var])
+
+ ## Is the object among the list of objects to debug?
+ if (not '*' in self.debug_obj and
+ not str(callerobject.__class__.__name__) in self.debug_obj):
+ return
+
+ message = str(message)
+
+ def breaklines(x):
+ '''
+ Helper function to keep width of the debugging output.
+
+ This may look ugly for arrays but it is acceptable and not
+ breaking the line would break the output format
+ '''
+ ## Get the number of lines we need (rounded down)
+ lines = len(x) // 60
+ if lines > 0:
+ for j in range(lines):
+ ## Print line with continuation marker
+ print >> self.debug_out, ls + '// ' + x[0:60] + ' \\'
+ ## Remove printed characters from output
+ x = x[60:]
+ ## Print final line
+ print >> self.debug_out, ls + '// ' + x
+
+ if self.debug_vrb == 1:
+ # Top line indicates class and method
+ c = ''
+ if callerobject:
+ c += 'Class: ' + str(callerobject.__class__.__name__) + ' | '
+ if callermethod:
+ c += 'Method: ' + str(callermethod)
+ print >> self.debug_out, '// ' + c
+ # Selected variables follow
+ if callerlocals:
+ for i,j in callerlocals.items():
+ print >> self.debug_out, '// ' \
+ + self.maybe_color('turquoise', str(i)) + ':' + str(j)
+ # Finally the message
+ print >> self.debug_out, self.maybe_color('yellow', message)
+ return
+
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '/////////////////////////////////' + \
+ '////////////////////////////////'
+
+ # General information about what is being debugged
+ #(module name or similar)
+ print >> self.debug_out, ls + '// ' + self.debug_env
+ print >> self.debug_out, ls + '//-----------------------------------' + \
+ '----------------------------'
+
+ ## If the caller is a class print the name here
+ if callerobject:
+ print >> self.debug_out, ls + \
+ '// Object Class: ' + str(callerobject.__class__.__name__)
+
+ ## If the method has been extracted print it here
+ if callermethod:
+ print >> self.debug_out, ls + '// ' \
+ + self.maybe_color('green', 'Method: ') + str(callermethod)
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '//---------------------------' + \
+ '------------------------------------'
+
+ ## Print the information on all available local variables
+ if callerlocals:
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '//'
+ print >> self.debug_out, ls + '// VALUES '
+ for i,j in callerlocals.items():
+ print >> self.debug_out, ls + '// ------------------> ' \
+ + self.maybe_color('turquoise', str(i)) + ':'
+ breaklines(str(j))
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '//------------------------------'\
+ '---------------------------------'
+
+ # Finally print the message
+ breaklines(self.maybe_color('yellow', message))
+
+ if self.debug_vrb == 3:
+ print >> self.debug_out, ls + '//-------------------------------' + \
+ '--------------------------------'
+ print >> self.debug_out, ls + '/////////////////////////////////' + \
+ '////////////////////////////////'
+
+## gloabal message handler
+OUT = Message('layman')