Package pythonutils :: Module standout
[hide private]
[frames] | no frames]

Source Code for Module pythonutils.standout

  1  # 2005/01/06 
  2  # v2.1.0 
  3   
  4  # A flexible object to redirect standard output and standard error 
  5  # Allows logging to a file and to set a level of verbosity 
  6   
  7  # Copyright Michael Foord, 2004. 
  8  # Released subject to the BSD License 
  9  # Please see http://www.voidspace.org.uk/python/license.shtml 
 10   
 11  # For information about bugfixes, updates and support, please join the Pythonutils mailing list. 
 12  # http://groups.google.com/group/pythonutils/ 
 13  # Comments, suggestions and bug reports welcome. 
 14  # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml 
 15  # E-mail fuzzyman@voidspace.org.uk 
 16   
 17  """ 
 18  StandOut - the Flexible Output Object (FOO !) 
 19  Adds optional logging to a file and setting of verbosity levels to the stdout stream 
 20  This means that, for the most part, standard print statments can be used throughout 
 21  your program and StandOut handles the rest. 
 22   
 23  Your user can choose a 'verbosity' level (how much information they want to receive), you give your messages 
 24  a priority level, and only messages with a high enough priority are actually displayed. 
 25   
 26  A simple way of implementing varying degrees of verbosity. 
 27  Additionally the output can be captured to a log file with no extra work. 
 28  (simply pass in a filename when you craete the object and anything printed is sent to the file as well) 
 29   
 30  StandOut can now be used with sys.stderr as well as sys.stdout. 
 31  This includes logging both sys.stdout *and* sys.stderr to the same file. 
 32  See the sys.stderr section at the bottom of this. 
 33   
 34  SIMPLE USAGE 
 35  (also see the tests which illustrate usage). 
 36   
 37  stout = StandOut(verbosity=verbositylevel) 
 38   
 39  or to log to a file : 
 40  stout = StandOut(filename='log.txt') 
 41   
 42  The verbosity level can be changed at any time by setting stout.verbosity : 
 43  stout.verbosity = 6 
 44   
 45  The priority of messages defaults to 5. This can be changed by setting 
 46  stout.priority = 6 *or* 
 47  print '&priority-6;' 
 48   
 49  The priority of an individual line can be set by *starting* the line with a priority marker : 
 50  print '&priority-6;This text has a priority 6.' 
 51  *or* by using the stout.write() method with a priority value: 
 52  stout.write('This text has a priority 6.\n', 6) 
 53  (notice you must add the '\n' when using the stout.write method.) 
 54   
 55  Only messages with a priority equal to or greater than the current verbosity level will be printed. 
 56  e.g. if  stout.verbosity = 6 
 57  (or the stout object was created using stout=StandOut(verbosity=6) ) 
 58  Only messages with a priority of 6 or above will be printed. 
 59   
 60  stout.write('This won't get printed\n, 5) 
 61  print '&priority-4;Nor will this' 
 62  stout.write('But this will\n', 6) 
 63  print '&priority-7;And so will this' 
 64   
 65  If for *any* reason you want to *actually* print a '&priority-n' marker at the start of a line 
 66  then you can escape it with a '&priority-e;' : 
 67  print '&priority-e;&priority-1;' 
 68  will actually print : 
 69  &priority-1; 
 70   
 71  StandOut will log to a file as well. 
 72  Set this by passing in a filename=filename keyword when you create the object *or* by setting stout.filename at any time. 
 73  The file has it's own priority, stout.file_verbosity. 
 74  Again this can be set when the object is created and/or changed at any time. See the full docs below. 
 75   
 76  This means your user can set a verbosity level (at the command line probably), you give each message a priority 
 77  setting and just use normal print statements in your program. 
 78  Only messages above your user's setting are actually displayed. 
 79  You can also set the log file to have a different priority threshhold to what is printed to the screen. 
 80  (So either less or more is logged to the file than is displayed at runtime.) 
 81   
 82  You can also pass in another function which can be used to display messages with (e.g. to a GUI window or whatever). 
 83  It also has it's own priority setting. 
 84   
 85  Any output method can be silenced by setting it to 0 
 86  All output can be silenced by setting the priority to 0 
 87   
 88  The stdout stream can be restored and any log file closed by calling stout.close() 
 89   
 90  verbosity = 1 is the highest 
 91  verbosity = 9 is the lowest     (only messages of priority 9 are printed) 
 92  verbosity = 0 is special - it switches off printing altogether 
 93   
 94  LIST OF KEYWORDS AND METHODS 
 95   
 96  StandOut Possible keyword arguments (with defaults shown) are : 
 97  (The following keywords also map to attributes of the StandOut object which can be read or set) 
 98  priority = 5 
 99  verbosity = 5 
100  filename = None 
101  file_verbosity = 5 
102  file_mode = 'w' 
103  print_fun = None 
104  printfun_verbosity = 5 
105   
106  Keyword arguments should either be passed in as a dictionary *or* as keywords when the object is created. 
107  If a dictionary is passed in, any other keywords will be ignored. 
108  Any missing keywords will use the defaults. 
109   
110  Methods ( stout = StandOut() ): 
111  stout.close() 
112  stout.write(line, priority) 
113  stout.set_print(function) 
114  stout.setall(verbosity) 
115   
116  the original stdout can be reached using : 
117  stout.output.write() 
118   
119  **NOTE** normal print statements make two calls to stdout.write(). Once for the text you are printing and another for the 
120  trailing '\n' or ' '. StandOut captures this to make sure the trailing '\n' or ' ' is printed at the same priority 
121  as the original line. This means you shouldn't use stout.write(line) where line uses the '&priority-n;' markers. 
122  (Because stout.write(line) only makes one call, not two). 
123  Either call stout.write(line, priority) to set a priority for that line. 
124  *or* set stout.priority directly. 
125   
126   
127   
128  EXPLANATION OF KEYWORDS AND METHODS 
129   
130  priority = 5 
131  This sets the priority for messages. 
132  If priority is 5 - then only output methods with a 'verbosity' of 5 or lower will display them. 
133  This value can later be set by adjusting the stout.priority attribute or using the priority markers. 
134   
135  verbosity = 5 
136  This is the verbosity level for messages to be printed to the screen. 
137  If the verbosity is 5 then only messages with a priority of 5 or higher will be sent to the screen. 
138  (Like a normal print statement). 
139  You can nadjust this at stout.verbosity 
140   
141  filename = None 
142  If you pass in a filename when you create the object it will be used as a logfile. 
143  It has it's own 'verbosity' level called 'file_verbosity'. 
144  If you don't pass in a filename, you can later add one by setting stout.filename 
145  Changing stout.filename after you have already set one is a bad thing to do :-) 
146   
147  file_verbosity = 5 
148  This is the verbosity level of the log file. 
149  Only messages with a priority higher than this will be sent to the logfile. 
150   
151  print_fun = None 
152  If you pass in a function (that takes one parameter - the line to be printed) this will be used to print as well. 
153  The function *isn't* stored at stout.print_fun - this value is just set to True to say we have a function. 
154  This could be used for displaying to the output window of a GUI, for example. 
155  If you want to pass in a function after obect creation then use the stout.set_print(function) method. 
156  You musn't have print statements in your function or you will get stuck in a loop (call stout.output.write(line) instead) 
157   
158  printfun_verbosity = 5 
159  Any function you pass in also has it's own verbosity setting - printfun_verbosity. 
160   
161  stream = 'output' 
162  By default StandOut will divert the sys.stdout stream. Set to 'error' to divert the sys.stderr 
163   
164  share = False 
165  You can divert both sys.stdout and sys.stderr. You can log both to the same file. 
166  Set a filename for your sys.stdout object and set share = True for your sys.stderr object. 
167  Any lines sent to sys.stderr will have a prefix attached to them. See 'error_marker' 
168   
169  error_marker = '[err] ' 
170  This is the marker put before every line logged from sys.stderr. 
171  It only applies if share is on - this means both streams are logged to the same file. 
172   
173  stout.close() 
174  When your program has finished with the obejct it should call stout.close() which restores sy.stdout and 
175  closes any logfile we have been using. 
176   
177  stout.write(line, priority) 
178  This can be used as an alternative way of specifying a priority for an individual line. 
179  It leaves stout.priority unaffected. 
180  Any calls to stout.write must have '\n' at the end if you want it to end with a newline. 
181  If you don't specify a priority then it behaves like sys.stdout.write would. 
182  Don't use priority markers with this and method and you can't use  priority = 0 (the priority setting will be ignored) 
183   
184  stout.set_print(function) 
185  This is used to pass in an additional printing function after the object has been created. 
186   
187  stout.setall(verbosity) 
188  Thisis a quick way of changing the verbosity for all three output methods. 
189   
190  Setting verbosity, file_verbosity or printfun_verbosity to 0 disables that ouput method. 
191  Setting priority to 0 switches off all output. 
192   
193  If you want to print to stdout directly and bypass the stout object for any reason - it is saved at stout.output 
194  Calls to stout.output.write(line) have the same effect that sys.stdout.write(line) would have had. 
195   
196  PRIORITY MARKERS 
197   
198  As well as directly setting stout.priority and using stout.write(line, priority) 
199  You can set the priority of a individual  line *or* change the general priority setting just using print statements. 
200  This is using 'priority markers'. 
201   
202  print '&priority-n;'  # sets the priority to n, where n is 0-9 
203  print '&priority-n;The stuff to print'      # sets the priority of just that line to n 
204   
205  If you actually want to print '&priority-n;' at the start of a line then you should escape it by putting '&priority-e;' 
206  in front of it. '&priority-e;' can also be escaped in the same way ! 
207   
208  Don't use priority markers if you are making direct calls to stout.write() 
209  use stout.write(line, priority) to set the priority of an individual line 
210  or alter stout.priority to adjust the general priority. 
211   
212   
213  sys.stderr 
214   
215  StandOut can now be used to divert sys.stderr as well as sys.stdout. 
216  To create an output object that does for sys.stderr *exactly* the same as we would do for sys.stdout use : 
217  stout2 = StandOut(stream='error') 
218   
219  It can log to a file and has all the properties that we had for sys.stdout. 
220  If you wanted to log to the *same* file as you are using for sys.stdout you *can't* just pass it the same filename. 
221  The two objects would both try to have a write lock on the same file. 
222   
223  What you do is pass the 'share' keyword to the error object when you create it : 
224   
225  stout = StandOut(filename='log.txt') 
226  stout2 = StandOut(stream='error', share=True) 
227   
228  Anything sent to sys.stdout *or* sys.stderr will now be logged in the 'log.txt' file. 
229  Every line sent to sys.stderr will be prefixed with '[err] ' which is the default error marker. 
230  You can adjust this with the 'error_marker' keyword. 
231   
232  stout2 = StandOut(stream='error', share=True, error_marker='**ERROR** ') 
233   
234  """ 
235   
236  __all__ = ['StandOut'] 
237   
238  import sys 
239   
240 -class StandOut:
241 242 stdout = None 243 stderr = None 244
245 - def __init__(self, indict=None, **keywargs):
246 """StandOut - the Flexible Output Object (FOO !)""" 247 if indict is None: 248 indict = {} 249 # 250 defaults = { 251 'priority': 5, 252 'verbosity': 5, 253 'filename': None, 254 'file_verbosity': 5, 255 'file_mode': 'w', 256 'print_fun': None, 257 'printfun_verbosity': 5 , 258 'stream': 'output', 259 'share': False, 260 'error_marker': '[err] ' 261 } 262 # 263 if not indict: 264 indict = keywargs 265 for value in defaults: 266 if not indict.has_key(value): 267 indict[value] = defaults[value] 268 # 269 if indict['stream'].lower() == 'error': 270 self.output = sys.stderr 271 sys.stderr = StandOut.stderr = self 272 self.stream = indict['stream'].lower() 273 else: 274 self.output = sys.stdout 275 sys.stdout = StandOut.stdout = self 276 self.stream = indict['stream'].lower() 277 278 self.filename = indict['filename'] 279 if self.filename: 280 self.filehandle = file(self.filename, indict['file_mode']) 281 else: 282 self.filehandle = None 283 284 self.file_mode = indict['file_mode'] 285 self.share = indict['share'] 286 self.err_marker = indict['error_marker'] 287 self.done_linefeed = True 288 289 self.priority = indict['priority'] # current message priority 290 self.file_verbosity = indict['file_verbosity'] # file output threshold 291 self.verbosity = indict['verbosity'] # stdout threshhold 292 self.printfun_verbosity = indict['printfun_verbosity'] # print_fun threshold 293 294 if indict['print_fun']: # set up the print_fun if we have been given one 295 self.print_fun = True 296 self.thefun = [indict['print_fun']] 297 else: 298 self.print_fun = False 299 300 self.markers = {} 301 for num in range(10): # define the markers 302 thismarker = '&priority-' + str(num) + ';' 303 self.markers[thismarker] = num 304 self.escapemarker = '&priority-e;' 305 306 self.skip = 0 307 self._lastpriority = 0
308 309 ######################################################################### 310 # public methods - available as methods of any instance of StandOut you create 311
312 - def write(self, line, priority = 0):
313 """Print to any of the output methods we are using. 314 Capture lines which set priority.""" 315 316 if self.skip: # if the last line was a priority marker then self.skip is set and we should miss the '\n' or ' ' that is sent next 317 self.skip = 0 318 return 319 320 if not priority: 321 if self._lastpriority: # if the last line had a priority marker at the start of it, then the '\n' or ' ' that is sent next should have the same priority 322 priority = self._lastpriority 323 self._lastpriority = 0 324 else: 325 priority = self.priority 326 327 if line in self.markers: # if the line is a priority marker 328 self.skip = 1 # either a '\n' or a ' ' will now be sent to sys.stdout.write() by print 329 self.priority = self.markers[line] 330 return 331 332 if line[:12] in self.markers: # if the line starts with a priority marker 333 priority = int(line[10]) # the priority of this line is at position 10 334 self._lastpriority = priority # set this value so that the '\n' or ' ' that follows also has the same priority 335 line = line[12:] # chop off the marker 336 elif line[:12] == self.escapemarker: 337 line = line[12:] # this just removes our 'escape marker' 338 339 if not priority: # if priority is set to 0 then we mute all output 340 return 341 342 if self.filename and not self.filehandle: # if a filename has been added since we opened 343 self.filehandle = file(self.filename, self.file_mode) 344 if self.filehandle and self.file_verbosity and priority >= self.file_verbosity: # if we have a file and file_verbosity is high enough to output 345 self.filehandle.write(line) 346 347 if self.verbosity and priority >= self.verbosity: # if verbosity is set high enough we print 348 if self.share and self.stream == 'error' and hasattr(StandOut.stdout, 'filename'): # if we are the error stream *and* share is on *and* stdout has a filename attribute.. 349 if self.done_linefeed: 350 StandOut.stdout.filehandle.write(self.err_marker) 351 self.done_linefeed = False 352 if line.endswith('\n'): 353 self.done_linefeed = True 354 line = line[:-1] 355 line = line.replace('\n', '\n' + self.err_marker) 356 if self.done_linefeed: 357 line = line + '\n' 358 StandOut.stdout.filehandle.write(line) # if 'share' is on we log to stdout file as well as print 359 # StandOut.stdout.output.write('hello') 360 self.output.write(line) 361 # if we have a print function set and it's priority is high enough 362 if self.print_fun and self.printfun_verbosity and priority >= self.printfun_verbosity: 363 self.use_print(line)
364
365 - def close(self):
366 """Restore the stdout stream and close the logging file if it's open.""" 367 if self.stream == 'error': 368 sys.stderr = self.output 369 else: 370 sys.stdout = self.output 371 if self.filename and self.filehandle: 372 self.filehandle.close() 373 del self.filehandle
374
375 - def set_print(self, print_fun):
376 """Set a new print_fun.""" 377 self.print_fun = True 378 self.thefun = [print_fun]
379
380 - def setall(self, verbosity):
381 """Sets the verbosity level for *all* the output methods.""" 382 self.verbosity = self.file_verbosity = self.printfun_verbosity = verbosity
383
384 - def flush(self):
385 return self.output.flush()
386
387 - def writelines(self, inline):
388 for line in inlines: 389 self.write(line)
390
391 - def __getattr__(self, attribute):
392 if not self.__dict__.has_key(attribute) or attribute == '__doc__': 393 return getattr(self.output, attribute) 394 return self.__dict__[attribute]
395 396 ########################################################## 397 # private methods, you shouldn't need to call these directly
398 - def use_print(self, line):
399 """A wrapper function for the function passed in as 'print_fun'.""" 400 self.thefun[0](line)
401 402 403 if __name__ == '__main__': 404 405 test = StandOut() 406 print 'hello' 407 test.priority = 4 408 print "You shouldn't see this" 409 test.verbosity = 4 410 print 'You should see this' 411 test.priority = 0 412 print 'but not this' 413 test.write('And you should see this\n', 5) 414 print 'but not this' 415 test.filename = 'test.txt' 416 test.priority = 5 417 test.setall(5) 418 print 'This should go to the file test.txt as well as the screen.' 419 test.file_verbosity = 7 420 print '&priority-8;' 421 print 'And this should be printed to both' 422 print '&priority-6;But this should only go to the screen.' 423 print 'And this should be printed to both, again.' 424 425 def afunction(line): 426 test.output.write('\nHello\n')
427 428 test.set_print(afunction) 429 print "We're now using another print function - which should mirror 'hello' to the screen." 430 print "In practise you could use it to send output to a GUI window." 431 print "Or perhaps format output." 432 433 test2 = StandOut(stream='error', share=True) # anything printed to sys.stderr, should now be logged to the stdout file as well 434 sys.stderr.write('Big Mistake') 435 sys.stderr.write('\n') 436 sys.stderr.write('Another Error') 437 sys.stderr.write('\n') 438 439 test.close() 440 test2.close() 441 print 'Normality is now restored' 442 print 'Any further problems, are entirely your own.' 443 444 """ 445 ISSUES/TODO 446 =========== 447 448 Doctests 449 450 Could trap when stout.write(line) is used with a priority marker. (By checking 451 for the default value of priority). 452 453 Could add a ``both`` keyword argument to avoid having to use the `share`` 454 keyword argument. It can just instantiate another StandOut itself. 455 456 CHANGELOG 457 ========= 458 459 2005/01/06 Version 2.1.0 460 Added flush and writelines method. 461 Added the 'stream' keyword for diverting the sys.stderr stream as well. 462 Added __getattr__ for any undefined methods. 463 Added the 'share' and 'error_marker' keywords for logging sys.stderr to the same file as sys.stdout. 464 465 07-04-04 Version 2.0.0 466 A complete rewrite. It now redirects the stdout stream so that normal print statements can be used. 467 Much better. 468 469 06-04-04 Version 1.1.0 470 Fixed a bug in passing in newfunc. Previously it only worked if you had a dummy variable for self. 471 """ 472