modules/xmpp/debug.py
changeset 0 93b25987d3e5
equal deleted inserted replaced
-1:000000000000 0:93b25987d3e5
       
     1 ##   debug.py 
       
     2 ##
       
     3 ##   Copyright (C) 2003 Jacob Lundqvist
       
     4 ##
       
     5 ##   This program is free software; you can redistribute it and/or modify
       
     6 ##   it under the terms of the GNU Lesser General Public License as published
       
     7 ##   by the Free Software Foundation; either version 2, or (at your option)
       
     8 ##   any later version.
       
     9 ##
       
    10 ##   This program is distributed in the hope that it will be useful,
       
    11 ##   but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12 ##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13 ##   GNU Lesser General Public License for more details.
       
    14 
       
    15 _version_ = '1.4.0'
       
    16 
       
    17 """\
       
    18 
       
    19 Generic debug class
       
    20 
       
    21 Other modules can always define extra debug flags for local usage, as long as
       
    22 they make sure they append them to debug_flags
       
    23 
       
    24 Also its always a good thing to prefix local flags with something, to reduce risk
       
    25 of coliding flags. Nothing breaks if two flags would be identical, but it might 
       
    26 activate unintended debugging.
       
    27 
       
    28 flags can be numeric, but that makes analysing harder, on creation its
       
    29 not obvious what is activated, and when flag_show is given, output isnt
       
    30 really meaningfull.
       
    31 
       
    32 This Debug class can either be initialized and used on app level, or used independantly
       
    33 by the individual classes.
       
    34 
       
    35 For samples of usage, see samples subdir in distro source, and selftest
       
    36 in this code
       
    37     
       
    38 """
       
    39 
       
    40 
       
    41 
       
    42 import sys
       
    43 import traceback
       
    44 import time
       
    45 import os
       
    46 
       
    47 import types
       
    48 
       
    49 if os.environ.has_key('TERM'):
       
    50     colors_enabled=True
       
    51 else:
       
    52     colors_enabled=False
       
    53 
       
    54 color_none         = chr(27) + "[0m"
       
    55 color_black        = chr(27) + "[30m"
       
    56 color_red          = chr(27) + "[31m"
       
    57 color_green        = chr(27) + "[32m"
       
    58 color_brown        = chr(27) + "[33m"
       
    59 color_blue         = chr(27) + "[34m"
       
    60 color_magenta      = chr(27) + "[35m"
       
    61 color_cyan         = chr(27) + "[36m"
       
    62 color_light_gray   = chr(27) + "[37m"
       
    63 color_dark_gray    = chr(27) + "[30;1m"
       
    64 color_bright_red   = chr(27) + "[31;1m"
       
    65 color_bright_green = chr(27) + "[32;1m"
       
    66 color_yellow       = chr(27) + "[33;1m"
       
    67 color_bright_blue  = chr(27) + "[34;1m"
       
    68 color_purple       = chr(27) + "[35;1m"
       
    69 color_bright_cyan  = chr(27) + "[36;1m"
       
    70 color_white        = chr(27) + "[37;1m"
       
    71 
       
    72 
       
    73 """
       
    74 Define your flags in yor modules like this:
       
    75 
       
    76 from debug import *
       
    77 
       
    78 DBG_INIT = 'init'                ; debug_flags.append( DBG_INIT )
       
    79 DBG_CONNECTION = 'connection'    ; debug_flags.append( DBG_CONNECTION )
       
    80 
       
    81  The reason for having a double statement wis so we can validate params
       
    82  and catch all undefined debug flags
       
    83  
       
    84  This gives us control over all used flags, and makes it easier to allow
       
    85  global debugging in your code, just do something like
       
    86  
       
    87  foo = Debug( debug_flags )
       
    88  
       
    89  group flags, that is a flag in it self containing multiple flags should be
       
    90  defined without the debug_flags.append() sequence, since the parts are already
       
    91  in the list, also they must of course be defined after the flags they depend on ;)
       
    92  example:
       
    93 
       
    94 DBG_MULTI = [ DBG_INIT, DBG_CONNECTION ]
       
    95 
       
    96 
       
    97 
       
    98   NoDebug
       
    99   -------
       
   100   To speed code up, typically for product releases or such
       
   101   use this class instead if you globaly want to disable debugging
       
   102 """
       
   103 
       
   104 
       
   105 class NoDebug:
       
   106     def __init__( self, *args, **kwargs ):
       
   107         self.debug_flags = []
       
   108     def show( self,  *args, **kwargs):
       
   109         pass
       
   110     def Show( self,  *args, **kwargs):
       
   111         pass
       
   112     def is_active( self, flag ):
       
   113         pass
       
   114     colors={}
       
   115     def active_set( self, active_flags = None ):
       
   116         return 0
       
   117     
       
   118 
       
   119 LINE_FEED = '\n'
       
   120 
       
   121 
       
   122 class Debug:      
       
   123     def __init__( self,
       
   124                   #
       
   125                   # active_flags are those that will trigger output
       
   126                   #
       
   127                   active_flags = None,
       
   128                   #
       
   129                   # Log file should be file object or file namne
       
   130                   #
       
   131                   log_file = sys.stderr,
       
   132                   #
       
   133                   # prefix and sufix can either be set globaly or per call.
       
   134                   # personally I use this to color code debug statements
       
   135                   # with prefix = chr(27) + '[34m'
       
   136                   #      sufix = chr(27) + '[37;1m\n'
       
   137                   #
       
   138                   prefix = 'DEBUG: ',
       
   139                   sufix = '\n',
       
   140                   #
       
   141                   # If you want unix style timestamps, 
       
   142                   #  0 disables timestamps
       
   143                   #  1 before prefix, good when prefix is a string
       
   144                   #  2 after prefix, good when prefix is a color
       
   145                   #
       
   146                   time_stamp = 0,
       
   147                   #
       
   148                   # flag_show should normaly be of, but can be turned on to get a
       
   149                   # good view of what flags are actually used for calls,
       
   150                   # if it is not None, it should be a string
       
   151                   # flags for current call will be displayed 
       
   152                   # with flag_show as separator                  
       
   153                   # recomended values vould be '-' or ':', but any string goes
       
   154                   #
       
   155                   flag_show = None,
       
   156                   #
       
   157                   # If you dont want to validate flags on each call to
       
   158                   # show(), set this to 0
       
   159                   #
       
   160                   validate_flags = 1,
       
   161                   #
       
   162                   # If you dont want the welcome message, set to 0
       
   163                   # default is to show welcome if any flags are active
       
   164                   welcome = -1
       
   165                   ):
       
   166         
       
   167         self.debug_flags = []
       
   168         if welcome == -1:
       
   169             if active_flags and len(active_flags):
       
   170                 welcome = 1
       
   171             else:
       
   172                 welcome = 0
       
   173             
       
   174         self._remove_dupe_flags()
       
   175         if log_file:
       
   176             if type( log_file ) is type(''):
       
   177                 try:
       
   178                     self._fh = open(log_file,'w')
       
   179                 except:
       
   180                     print 'ERROR: can open %s for writing'
       
   181                     sys.exit(0)
       
   182             else: ## assume its a stream type object
       
   183                 self._fh = log_file
       
   184         else:
       
   185             self._fh = sys.stdout
       
   186          
       
   187         if time_stamp not in (0,1,2):
       
   188             msg2 = '%s' % time_stamp
       
   189             raise 'Invalid time_stamp param', msg2
       
   190         self.prefix = prefix
       
   191         self.sufix = sufix
       
   192         self.time_stamp = time_stamp
       
   193         self.flag_show = None # must be initialised after possible welcome
       
   194         self.validate_flags = validate_flags
       
   195 
       
   196         self.active_set( active_flags )
       
   197         if welcome:
       
   198             self.show('')
       
   199             caller = sys._getframe(1) # used to get name of caller
       
   200             try:
       
   201                 mod_name= ":%s" % caller.f_locals['__name__']
       
   202             except:
       
   203                 mod_name = ""
       
   204             self.show('Debug created for %s%s' % (caller.f_code.co_filename,
       
   205                                                    mod_name ))
       
   206             self.show(' flags defined: %s' % ','.join( self.active ))
       
   207             
       
   208         if type(flag_show) in (type(''), type(None)):
       
   209             self.flag_show = flag_show
       
   210         else:
       
   211             msg2 = '%s' % type(flag_show )
       
   212             raise 'Invalid type for flag_show!', msg2
       
   213 
       
   214 
       
   215         
       
   216 
       
   217 
       
   218     def show( self, msg, flag = None, prefix = None, sufix = None,
       
   219               lf = 0 ):
       
   220         """
       
   221         flag can be of folowing types:
       
   222             None - this msg will always be shown if any debugging is on
       
   223             flag - will be shown if flag is active
       
   224             (flag1,flag2,,,) - will be shown if any of the given flags 
       
   225                                are active
       
   226 
       
   227         if prefix / sufix are not given, default ones from init will be used
       
   228         
       
   229         lf = -1 means strip linefeed if pressent
       
   230         lf = 1 means add linefeed if not pressent
       
   231         """
       
   232         
       
   233         if self.validate_flags:
       
   234             self._validate_flag( flag )
       
   235             
       
   236         if not self.is_active(flag):
       
   237             return
       
   238         if prefix:
       
   239             pre = prefix
       
   240         else:
       
   241             pre = self.prefix
       
   242         if sufix:
       
   243             suf = sufix
       
   244         else:
       
   245             suf = self.sufix
       
   246 
       
   247         if self.time_stamp == 2:
       
   248             output = '%s%s ' % ( pre,
       
   249                                  time.strftime('%b %d %H:%M:%S',
       
   250                                  time.localtime(time.time() )),
       
   251                                  )
       
   252         elif self.time_stamp == 1:
       
   253             output = '%s %s' % ( time.strftime('%b %d %H:%M:%S',
       
   254                                  time.localtime(time.time() )),
       
   255                                  pre,
       
   256                                  )
       
   257         else:
       
   258             output = pre
       
   259             
       
   260         if self.flag_show:
       
   261             if flag:
       
   262                 output = '%s%s%s' % ( output, flag, self.flag_show )
       
   263             else:
       
   264                 # this call uses the global default,
       
   265                 # dont print "None", just show the separator
       
   266                 output = '%s %s' % ( output, self.flag_show )
       
   267 
       
   268         output = '%s%s%s' % ( output, msg, suf )
       
   269         if lf:
       
   270             # strip/add lf if needed
       
   271             last_char = output[-1]
       
   272             if lf == 1 and last_char != LINE_FEED:
       
   273                 output = output + LINE_FEED
       
   274             elif lf == -1 and last_char == LINE_FEED:
       
   275                 output = output[:-1]
       
   276         try:
       
   277             self._fh.write( output )
       
   278         except:
       
   279             # unicode strikes again ;)
       
   280             s=u''
       
   281             for i in range(len(output)):
       
   282                 if ord(output[i]) < 128:
       
   283                     c = output[i]
       
   284                 else:
       
   285                     c = '?'
       
   286                 s=s+c
       
   287             self._fh.write( '%s%s%s' % ( pre, s, suf ))
       
   288         self._fh.flush()
       
   289             
       
   290                 
       
   291     def is_active( self, flag ):
       
   292         'If given flag(s) should generate output.'
       
   293 
       
   294         # try to abort early to quicken code
       
   295         if not self.active:
       
   296             return 0
       
   297         if not flag or flag in self.active:
       
   298             return 1
       
   299         else:
       
   300             # check for multi flag type:
       
   301             if type( flag ) in ( type(()), type([]) ):
       
   302                 for s in flag:
       
   303                     if s in self.active:
       
   304                         return 1
       
   305         return 0
       
   306 
       
   307     
       
   308     def active_set( self, active_flags = None ):
       
   309         "returns 1 if any flags where actually set, otherwise 0."
       
   310         r = 0
       
   311         ok_flags = []
       
   312         if not active_flags:
       
   313             #no debuging at all
       
   314             self.active = []
       
   315         elif type( active_flags ) in ( types.TupleType, types.ListType ):
       
   316             flags = self._as_one_list( active_flags )
       
   317             for t in flags:
       
   318                 if t not in self.debug_flags:
       
   319                     sys.stderr.write('Invalid debugflag given: %s\n' % t )
       
   320                 ok_flags.append( t )
       
   321                 
       
   322             self.active = ok_flags
       
   323             r = 1
       
   324         else:
       
   325             # assume comma string
       
   326             try:
       
   327                 flags = active_flags.split(',')
       
   328             except:
       
   329                 self.show( '***' )
       
   330                 self.show( '*** Invalid debug param given: %s' % active_flags )
       
   331                 self.show( '*** please correct your param!' )
       
   332                 self.show( '*** due to this, full debuging is enabled' )
       
   333                 self.active = self.debug_flags
       
   334             
       
   335             for f in flags:
       
   336                 s = f.strip()
       
   337                 ok_flags.append( s )
       
   338             self.active = ok_flags
       
   339 
       
   340         self._remove_dupe_flags()
       
   341         return r
       
   342     
       
   343     def active_get( self ):
       
   344         "returns currently active flags."
       
   345         return self.active
       
   346     
       
   347     
       
   348     def _as_one_list( self, items ):
       
   349         """ init param might contain nested lists, typically from group flags.
       
   350         
       
   351         This code organises lst and remves dupes
       
   352         """
       
   353         if type( items ) <> type( [] ) and type( items ) <> type( () ):
       
   354             return [ items ]
       
   355         r = []
       
   356         for l in items:
       
   357             if type( l ) == type([]):
       
   358                 lst2 = self._as_one_list( l )
       
   359                 for l2 in lst2: 
       
   360                     self._append_unique_str(r, l2 )
       
   361             elif l == None:
       
   362                 continue
       
   363             else:
       
   364                 self._append_unique_str(r, l )
       
   365         return r
       
   366     
       
   367     
       
   368     def _append_unique_str( self, lst, item ):
       
   369         """filter out any dupes."""
       
   370         if type(item) <> type(''):
       
   371             msg2 = '%s' % item
       
   372             raise 'Invalid item type (should be string)',msg2
       
   373         if item not in lst:
       
   374             lst.append( item )
       
   375         return lst
       
   376 
       
   377     
       
   378     def _validate_flag( self, flags ):
       
   379         'verify that flag is defined.'
       
   380         if flags:
       
   381             for f in self._as_one_list( flags ):
       
   382                 if not f in self.debug_flags:
       
   383                     msg2 = '%s' % f
       
   384                     raise 'Invalid debugflag given', msg2
       
   385 
       
   386     def _remove_dupe_flags( self ):
       
   387         """
       
   388         if multiple instances of Debug is used in same app, 
       
   389         some flags might be created multiple time, filter out dupes
       
   390         """
       
   391         unique_flags = []
       
   392         for f in self.debug_flags:
       
   393             if f not in unique_flags:
       
   394                 unique_flags.append(f)
       
   395         self.debug_flags = unique_flags
       
   396 
       
   397     colors={}
       
   398     def Show(self, flag, msg, prefix=''):
       
   399         msg=msg.replace('\r','\\r').replace('\n','\\n').replace('><','>\n  <')
       
   400         if not colors_enabled: pass
       
   401         elif self.colors.has_key(prefix): msg=self.colors[prefix]+msg+color_none
       
   402         else: msg=color_none+msg
       
   403         if not colors_enabled: prefixcolor=''
       
   404         elif self.colors.has_key(flag): prefixcolor=self.colors[flag]
       
   405         else: prefixcolor=color_none
       
   406         
       
   407         if prefix=='error':
       
   408             _exception = sys.exc_info()
       
   409             if _exception[0]:
       
   410                 msg=msg+'\n'+''.join(traceback.format_exception(_exception[0], _exception[1], _exception[2])).rstrip()
       
   411         
       
   412         prefix= self.prefix+prefixcolor+(flag+' '*12)[:12]+' '+(prefix+' '*6)[:6]
       
   413         self.show(msg, flag, prefix)
       
   414 
       
   415     def is_active( self, flag ):
       
   416         if not self.active: return 0
       
   417         if not flag or flag in self.active and DBG_ALWAYS not in self.active or flag not in self.active and DBG_ALWAYS in self.active : return 1
       
   418         return 0
       
   419 
       
   420 DBG_ALWAYS='always'
       
   421 
       
   422 ##Uncomment this to effectively disable all debugging and all debugging overhead.
       
   423 #Debug=NoDebug