1 #################################################################################
2 # LAYMAN - DEBUGGING FUNCTIONS
3 #################################################################################
4 # debug.py -- Utility function for debugging
5 # Copyright 2005 - 2008 Gunnar Wrobel
6 # Distributed under the terms of the GNU General Public License v2
8 __version__
= "$Id: debug.py 153 2006-06-05 06:03:16Z wrobel $"
10 #################################################################################
14 #################################################################################
16 import sys
, inspect
, types
18 from optparse
import OptionGroup
20 #################################################################################
22 ## Color codes (taken from portage)
24 #################################################################################
29 codes
['reset'] = esc_seq
+ '39;49;00m'
30 codes
['red'] = esc_seq
+ '31;01m'
31 codes
['green'] = esc_seq
+ '32;01m'
32 codes
['yellow'] = esc_seq
+ '33;01m'
33 codes
['turquoise'] = esc_seq
+ '36;01m'
35 #################################################################################
39 #################################################################################
42 #FIXME: Think about some simple doctests before you modify this class the
45 def __init__(self
, module
= '',
49 debugging_verbosity
= 2,
57 # A description of the module that is being debugged
58 self
.debug_env
= module
60 # Where should the debugging output go? This can also be a file
63 # Where should the error output go? This can also be a file
66 # The higher the level the more information you will get
67 self
.warn_lev
= warn_level
69 # The higher the level the more information you will get
70 self
.info_lev
= info_level
72 # The highest level of debugging messages acceptable for output
73 # The higher the level the more output you will get
74 self
.debug_lev
= debugging_level
76 # The debugging output can range from very verbose (3) to
78 self
.debug_vrb
= debugging_verbosity
80 # Which methods should actually be debugged?
81 # Use '*' to indicate 'All methods'
84 # Which objects should actually be debugged?
85 # Use '*' to indicate 'All objects'
88 # Which variables should actually be debugged?
89 # Use '*' to indicate 'All variables'
92 # Exclude class variables by default
93 self
.show_class_variables
= False
95 # Should the output be colored?
98 self
.has_error
= False
101 ############################################################################
102 # Add command line options
104 def cli_opts(self
, parser
):
106 group
= OptionGroup(parser
,
107 '<Debugging options>',
108 'Control the debugging features of '
111 group
.add_option('--debug',
112 action
= 'store_true',
113 help = 'Activates debugging features.')
115 group
.add_option('--debug-level',
118 help = 'A value between 0 and 10. 0 means no debugging '
119 'messages will be selected, 10 selects all debugging me'
120 'ssages. Default is "4".')
122 group
.add_option('--debug-verbose',
125 help = 'A value between 1 and 3. Lower values yield les'
126 's verbose debugging output. Default is "2".')
128 group
.add_option('--debug-methods',
130 help = 'Limits the methods that will return debugging o'
131 'utput. The function name is sufficient and there is no'
132 'difference between class methods or general functions.'
133 ' Several methods can be specified by seperating them w'
134 ' with a comma. Default is "*" which specifies all meth'
137 group
.add_option('--debug-classes',
139 help = 'Limits the classes that will return debugging o'
140 'utput. Specify only the class name not including the m'
141 'odules in which the class is defined (e.g. MyModule.ma'
142 'in.Main should only be represented by "Main"). Several'
143 'classes can be specified by seperating them with a com'
144 'ma. Default is "*" which specifies all classes.')
146 group
.add_option('--debug-variables',
148 help = 'Limits the variables that will return debugging'
149 ' output. Several variables can be specified by seperat'
150 'ing them with a comma. Default is "*" which specifies '
153 group
.add_option('--debug-class-vars',
154 action
= 'store_true',
155 help = 'In default mode the debugging code will only re'
156 'turn information on the local variable which does not '
157 'include the class variables. Use this switch to add al'
158 'l values that are provided by "self".')
160 group
.add_option('--debug-nocolor',
161 action
= 'store_true',
162 help = 'Deactivates colors in the debugging output.')
164 parser
.add_option_group(group
)
167 #############################################################################
168 # Handle command line options
170 def cli_handle(self
, options
):
172 if (options
.__dict
__.has_key('debug')
173 and options
.__dict
__['debug']):
179 if (options
.__dict
__.has_key('debug_class_vars')
180 and options
.__dict
__['debug_class_vars']):
181 self
.class_variables_on()
183 self
.class_variables_off()
185 if (options
.__dict
__.has_key('debug_nocolor')
186 and options
.__dict
__['debug_nocolor']):
191 if (options
.__dict
__.has_key('debug_level') and
192 options
.__dict
__['debug_level']):
193 dbglvl
= int(options
.__dict
__['debug_level'])
198 self
.set_debug_level(dbglvl
)
200 if (options
.__dict
__.has_key('debug_verbose') and
201 options
.__dict
__['debug_verbose']):
202 dbgvrb
= int(options
.__dict
__['debug_verbose'])
207 self
.set_debug_verbosity(dbgvrb
)
209 for i
in [('debug_methods', self
.set_debug_methods
),
210 ('debug_classes', self
.set_debug_classes
),
211 ('debug_variables', self
.set_debug_variables
),]:
213 if (options
.__dict
__.has_key(i
[0]) and
214 options
.__dict
__[i
[0]]):
215 i
[1](options
.__dict
__[i
[0]])
218 #############################################################################
221 def set_module(self
, module
):
223 self
.debug_env
= module
225 def set_debug_methods(self
, methods
):
227 methods
= methods
.split(',')
230 self
.debug_mth
= methods
232 def set_debug_classes(self
, classes
):
234 classes
= classes
.split(',')
237 self
.debug_obj
= classes
239 def set_debug_variables(self
, variables
):
241 variables
= variables
.split(',')
244 self
.debug_var
= variables
246 def maybe_color (self
, col
, text
):
248 return codes
[col
] + text
+ codes
['reset']
251 def set_info_level(self
, info_level
= 4):
252 self
.info_lev
= info_level
255 self
.set_info_level(0)
257 def info_on(self
, info_level
= 4):
258 self
.set_info_level(info_level
)
260 def set_warn_level(self
, warn_level
= 4):
261 self
.warn_lev
= warn_level
264 self
.set_warn_level(0)
266 def warn_on(self
, warn_level
= 4):
267 self
.set_warn_level(warn_level
)
269 def set_debug_level(self
, debugging_level
= 4):
270 self
.debug_lev
= debugging_level
272 def set_debug_verbosity(self
, debugging_verbosity
= 2):
273 self
.debug_vrb
= debugging_verbosity
276 self
.set_debug_level(0)
279 self
.set_debug_level()
282 self
.use_color
= False
285 self
.use_color
= True
287 def class_variables_off(self
):
288 self
.show_class_variables
= False
290 def class_variables_on(self
):
291 self
.show_class_variables
= True
293 #############################################################################
296 def notice (self
, note
):
299 def info (self
, info
, level
= 4):
301 if type(info
) not in types
.StringTypes
:
304 if level
> self
.info_lev
:
307 for i
in info
.split('\n'):
308 print self
.maybe_color('green', '* ') + i
310 def status (self
, message
, status
, info
= 'ignored'):
312 if type(message
) not in types
.StringTypes
:
313 message
= str(message
)
315 lines
= message
.split('\n')
320 for i
in lines
[0:-1]:
321 print self
.maybe_color('green', '* ') + i
329 result
= '[' + self
.maybe_color('green', 'ok') + ']'
331 result
= '[' + self
.maybe_color('red', 'failed') + ']'
333 result
= '[' + self
.maybe_color('yellow', info
) + ']'
335 print self
.maybe_color('green', '* ') + i
+ ' ' + '.' * (58 - len(i
)) \
338 def warn (self
, warn
, level
= 4):
340 if type(warn
) not in types
.StringTypes
:
343 if level
> self
.warn_lev
:
346 for i
in warn
.split('\n'):
347 print self
.maybe_color('yellow', '* ') + i
349 def error (self
, error
):
351 if type(error
) not in types
.StringTypes
:
354 for i
in error
.split('\n'):
355 print >> self
.error_out
, self
.maybe_color('red', '* ') + i
356 self
.has_error
= True
358 def die (self
, error
):
360 if type(error
) not in types
.StringTypes
:
363 for i
in error
.split('\n'):
364 self
.error(self
.maybe_color('red', 'Fatal error: ') + i
)
365 self
.error(self
.maybe_color('red', 'Fatal error(s) - aborting'))
368 def debug (self
, message
, level
= 4):
370 This is a generic debugging method.
372 ## Check the debug level first. This is the most inexpensive check.
373 if level
> self
.debug_lev
:
376 ## Maybe this should be debugged. So get the stack first.
377 stack
= inspect
.stack()
379 ## This can probably never happen but does not harm to check
380 ## that there is actually something calling this function
384 ## Get the stack length to determine indentation of the debugging output
385 stacklength
= len(stack
)
386 ls
= ' ' * stacklength
388 ## Get the information about the caller
391 ## The function name of the calling frame is the fourth item in the list
392 callermethod
= caller
[3]
394 ## Is this actually one of the methods that should be debugged?
395 if not '*' in self
.debug_mth
and not callermethod
in self
.debug_mth
:
398 ## Still looks like this should be debugged. So retrieve the dictionary
399 ## of local variables from the caller
400 callerlocals
= inspect
.getargvalues(caller
[0])[3]
402 ## Is the caller an obejct? If so he provides 'self'
403 if 'self' in callerlocals
.keys():
404 callerobject
= callerlocals
['self']
405 del callerlocals
['self']
406 if self
.show_class_variables
:
407 cv
= inspect
.getmembers(callerobject
,
408 lambda x
: not inspect
.ismethod(x
))
409 callerlocals
.sync(cv
)
413 # Remove variables not requested
414 if not '*' in self
.debug_var
:
415 callerlocals
= dict([i
for i
in callerlocals
.items()
416 if i
[0] in self
.debug_var
])
418 ## Is the object among the list of objects to debug?
419 if (not '*' in self
.debug_obj
and
420 not str(callerobject
.__class
__.__name
__) in self
.debug_obj
):
423 if type(message
) not in types
.StringTypes
:
424 message
= str(message
)
428 Helper function to keep width of the debugging output.
430 This may look ugly for arrays but it is acceptable and not
431 breaking the line would break the output format
433 ## Get the number of lines we need (rounded down)
436 for j
in range(lines
):
437 ## Print line with continuation marker
438 print >> self
.debug_out
, ls
+ '// ' + x
[0:60] + ' \\'
439 ## Remove printed characters from output
442 print >> self
.debug_out
, ls
+ '// ' + x
444 if self
.debug_vrb
== 1:
445 # Top line indicates class and method
448 c
+= 'Class: ' + str(callerobject
.__class
__.__name
__) + ' | '
450 c
+= 'Method: ' + str(callermethod
)
451 print >> self
.debug_out
, '// ' + c
452 # Selected variables follow
454 for i
,j
in callerlocals
.items():
455 print >> self
.debug_out
, '// ' \
456 + self
.maybe_color('turquoise', str(i
)) + ':' + str(j
)
457 # Finally the message
458 print >> self
.debug_out
, self
.maybe_color('yellow', message
)
461 if self
.debug_vrb
== 3:
462 print >> self
.debug_out
, ls
+ '/////////////////////////////////' + \
463 '////////////////////////////////'
465 # General information about what is being debugged
466 #(module name or similar)
467 print >> self
.debug_out
, ls
+ '// ' + self
.debug_env
468 print >> self
.debug_out
, ls
+ '//-----------------------------------' + \
469 '----------------------------'
471 ## If the caller is a class print the name here
473 print >> self
.debug_out
, ls
+ \
474 '// Object Class: ' + str(callerobject
.__class
__.__name
__)
476 ## If the method has been extracted print it here
478 print >> self
.debug_out
, ls
+ '// ' \
479 + self
.maybe_color('green', 'Method: ') + str(callermethod
)
480 if self
.debug_vrb
== 3:
481 print >> self
.debug_out
, ls
+ '//---------------------------' + \
482 '------------------------------------'
484 ## Print the information on all available local variables
486 if self
.debug_vrb
== 3:
487 print >> self
.debug_out
, ls
+ '//'
488 print >> self
.debug_out
, ls
+ '// VALUES '
489 for i
,j
in callerlocals
.items():
490 print >> self
.debug_out
, ls
+ '// ------------------> ' \
491 + self
.maybe_color('turquoise', str(i
)) + ':'
493 if self
.debug_vrb
== 3:
494 print >> self
.debug_out
, ls
+ '//------------------------------'\
495 '---------------------------------'
497 # Finally print the message
498 breaklines(self
.maybe_color('yellow', message
))
500 if self
.debug_vrb
== 3:
501 print >> self
.debug_out
, ls
+ '//-------------------------------' + \
502 '--------------------------------'
503 print >> self
.debug_out
, ls
+ '/////////////////////////////////' + \
504 '////////////////////////////////'
506 ## gloabal message handler
507 OUT
= Message('layman')