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


UCC Code Repository

Contents of /bunnyblog/modules/standout.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations) (download) (as text)
Tue Jan 29 14:32:01 2008 UTC (12 years, 2 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 svn-admin 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 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     """

Managed by UCC Webmasters ViewVC Help
Powered by ViewVC 1.1.26