Package gmisclib :: Module die
[frames] | no frames]

Source Code for Module gmisclib.die

  1  # -*- coding: utf-8 -*- 
  2  from __future__ import with_statement 
  3   
  4  """This class handles error logging. 
  5  Duplicate messages are lumped together. 
  6  It will remember notes that will be printed 
  7  if there is a later error/warning message. 
  8  (The notes will be printed in chronological order.) 
  9   
 10  The idea is that you call L{note} to memorize information that you might want 
 11  to know if there is an error.   Then if/when trouble happens, you call L{warn}, 
 12  L{die}, L{catch}, or L{catchexit} and the accumulated notes will be printed. 
 13   
 14  Calling L{info} or L{dbg} just prints out interesting stuff for your log files. 
 15  If you set C{L{debug}=False}, then calls to L{dbg} will do nothing. 
 16   
 17  Thanks to U{http://farmdev.com/talks/unicode/} for explanations of unicode. 
 18  """ 
 19   
 20  import os 
 21  import sys 
 22  import traceback 
 23  import threading 
 24  import g_ucode as U 
 25   
 26  debug = True    #: Set to False to disable dbg() messages. 
 27  nocompress = False #: False means that only the first of repeated messages are displayed; True displays all. 
 28  Threads = False 
 29   
 30  _name = os.path.basename(sys.argv[0]) 
 31  # _stderr = sys.stderr 
 32  # _stdout = sys.stdout 
 33  # If you need these, replace with _q.stderr and _q.stdout or _q.set_out_err() 
 34   
 35   
36 -class counter:
37 lock = threading.Lock() 38
39 - def __init__(self):
40 self.reps = 0 41 self.last = None 42 self.sequence = 0 43 self.memory = {} 44 self.stderr = sys.stderr 45 self.stdout = sys.stdout 46 assert self.stderr is not None 47 assert self.stdout is not None
48
49 - def set_out_err(self, stdout, stderr):
50 self.stdout.flush() 51 self.stderr.flush() 52 self.stdout = stdout 53 self.stderr = stderr
54
55 - def incoming(self, msgtype, name, s):
56 if Threads: 57 id = u'%s(%s)' % (msgtype, threading.currentThread().name) 58 else: 59 id = msgtype 60 t = (id, name, s) 61 # print "# t=", t, "last=", self.last 62 if nocompress or t != self.last: 63 self.showdump(u'%s: %s: %s\n' % (id, name, s)) 64 self.last = t 65 else: 66 self.clearmem() 67 self.reps += 1 68 assert self.stderr is not None 69 assert self.stdout is not None
70
71 - def showreps(self):
72 if self.last is not None and self.reps>1: 73 self.stderr.write(U.e(u'%s: last message repeated %d times.\n' 74 % (self.last[0], self.reps))) 75 self.last = None 76 self.reps = 1
77
78 - def showdump(self, s):
79 self.stdout.flush() 80 self.showreps() 81 self.dumpmem() 82 if s: 83 self.stderr.write(U.e(s)) 84 self.stderr.flush()
85
86 - def __del__(self):
87 self.showreps()
88
89 - def clearmem(self):
90 self.memory = {} 91 self.sequence = 0
92
93 - def dumpmem(self):
94 tmp = [(sqn, k, val) for (k, (sqn,val)) in self.memory.items()] 95 tmp.sort() 96 for (sqn,k,val) in tmp: 97 self.stderr.write(U.e(u'#NOTE: %s = %s\n' % (k, val))) 98 # Note that there is an obscure problem here. If k is non-ASCII, 99 # sometimes that sys.stderr cannot handle it, and will lead to 100 # an error. 101 self.clearmem()
102
103 - def memorize(self, key, value):
104 self.memory[key] = (self.sequence, value) 105 self.sequence += 1
106 107 _q = counter() 108
109 -def die(s):
110 """Output a fatal error message and terminate. 111 Before the error message, it summarizes all the calls to L{note}. 112 @param s: error message 113 @type s: str 114 """ 115 s = U.u(s) 116 e = u'ERR: %s: %s' % (_name, s) 117 exit(1, e)
118 119
120 -def warn(s):
121 """Output a non-fatal warning. 122 Before the warning, it summarizes all the calls to L{note}. 123 @param s: warning message 124 @type s: str 125 """ 126 global _q 127 with _q.lock: 128 _q.incoming(u'#WARN', _name, U.u(s))
129 130
131 -def info(s):
132 """Output useful information. 133 Before the message, it summarizes all the calls to L{note}. 134 @param s: message 135 @type s: str 136 """ 137 global _q 138 with _q.lock: 139 _q.incoming(u'#INFO', _name, U.u(s))
140 141
142 -def catch(extext=None):
143 """Call this inside an except statement. 144 It will report the exception and any other information it has. 145 """ 146 etype, value, tback = sys.exc_info() 147 if extext is None: 148 extext = u"die.catch: exception caught.\n" 149 with _q.lock: 150 _q.showdump(u'') 151 traceback.print_exception(etype, value, tback) 152 etype = None 153 value = None 154 tback = None 155 _q.stderr.flush() 156 _q.stdout.flush()
157 158
159 -def catchexit(extext=None, n=1, text=None):
160 """Call this inside an except statement. It will report 161 all information and then exit.""" 162 catch(extext) 163 exit(n, text=U.u(text))
164 165
166 -def dbg(s):
167 """Output debugging information, if debug is nonzero. 168 Before the debugging info, it prints all information given to L{note}. 169 @param s: debug message 170 @type s: str 171 """ 172 if debug: 173 global _q 174 _q.incoming(u'#DBG', _name, U.u(s))
175 176
177 -def exit(n, text=None):
178 """Exit, after dumping accumulated L{note}s. 179 @param n: the processes' exit code 180 @type n: int 181 @param text: something to print 182 @type text: L{str} or L{None} 183 """ 184 global _q 185 text = U.u(text) 186 with _q.lock: 187 _q.showdump(u'') 188 if text is not None: 189 _q.stdout.write(U.e(u'%s\n' % text)) 190 _q.stdout.flush() 191 _q.stderr.write(U.e(u'%s\n' % text)) 192 _q.stderr.flush() 193 sys.exit(n)
194 195
196 -def flush():
197 """Flush out any accumulated messages. Should be called shortly before exit. 198 @note: This matters when C{nocompress=False} under conditions where 199 L{sys.stderr} or L{sys.stdout} may be closed before the program 200 terminates. Under those conditions, you might get a 201 "last message repeated %d times" message produced into a closed 202 file descriptor, which would lead to an L{IOError}. 203 """ 204 _q.showreps() 205 _q.stdout.flush() 206 _q.stderr.flush()
207 208
209 -def note(key, value):
210 """Memorize a note, which will be output along with the next error/warning/info message. 211 These will be printed in the form '#NOTE key = value', with value converted to a string 212 at the time of printing. Note that only the most recent note is kept for each key. 213 @type key: str 214 @type value: anything 215 """ 216 global _q 217 with _q.lock: 218 _q.memorize(key, U.u(value))
219 220
221 -def get(key):
222 """Get the most recent value memorized by a call to L{note}. 223 @type key: str 224 @rtype: whatever was memorized 225 """ 226 global _q 227 with _q.lock: 228 try: 229 return _q.memory[key][1] 230 except KeyError: 231 pass 232 return None
233 234
235 -def uprint(s):
236 """Like a print statement, except it prints in utf-8 encoding instead of ASCII.""" 237 print U.e(s)
238
239 -def se_write(s):
240 """Like sys.stderr.write(), except it prints in utf-8 encoding instead of ASCII.""" 241 return sys.stderr.write(U.e(s))
242
243 -def so_write(s):
244 """Like sys.stdout.write(), except it prints in utf-8 encoding instead of ASCII.""" 245 return sys.stdout.write(U.e(s))
246 247 248 if __name__ == '__main__': 249 debug = 1 250 info("You should see a debug message [\xc4\x87] next.") 251 dbg("This is the debug message.") 252 debug = 0 253 note("gleep", "oldest note") 254 note("foo", "bar") 255 note("foo", "fleep") 256 note("foo", "foo") 257 note("farf", "newest note") 258 info("You should not see a debug message next.") 259 dbg("This is the debug message you shouldn't see.") 260 warn("This is a warning.") 261 warn("This is a warning.") 262 warn("This is a warning.") 263 info("It should have been repeated three times.") 264 die("This is the end.") 265