/[theodore]/bunnyblog/modules/standout.py


UCC Code Repository

Contents of /bunnyblog/modules/standout.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations) (download) (as text)
Tue Jan 29 14:32:01 2008 UTC (12 years, 7 months ago) by svn-admin
File MIME type: text/x-python
File size: 19750 byte(s)
Re-import of repository after repository database corruption.

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/documents/BSD-LICENSE.txt
10
11 # For information about bugfixes, updates and support, please join the Pythonutils mailing list.
12 # http://voidspace.org.uk/mailman/listinfo/pythonutils_voidspace.org.uk
13 # Comments, suggestions and bug reports welcome.
14 # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
15 # E-mail [email protected]
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 """

Managed by UCC Webmasters ViewVC Help
Powered by ViewVC 1.1.26