xmpp: import xmppy-0.5.0rc1
[uccdoor.git] / xmpp / debug.py
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

UCC git Repository :: git.ucc.asn.au