1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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']
290 self.file_verbosity = indict['file_verbosity']
291 self.verbosity = indict['verbosity']
292 self.printfun_verbosity = indict['printfun_verbosity']
293
294 if indict['print_fun']:
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):
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
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:
317 self.skip = 0
318 return
319
320 if not priority:
321 if self._lastpriority:
322 priority = self._lastpriority
323 self._lastpriority = 0
324 else:
325 priority = self.priority
326
327 if line in self.markers:
328 self.skip = 1
329 self.priority = self.markers[line]
330 return
331
332 if line[:12] in self.markers:
333 priority = int(line[10])
334 self._lastpriority = priority
335 line = line[12:]
336 elif line[:12] == self.escapemarker:
337 line = line[12:]
338
339 if not priority:
340 return
341
342 if self.filename and not self.filehandle:
343 self.filehandle = file(self.filename, self.file_mode)
344 if self.filehandle and self.file_verbosity and priority >= self.file_verbosity:
345 self.filehandle.write(line)
346
347 if self.verbosity and priority >= self.verbosity:
348 if self.share and self.stream == 'error' and hasattr(StandOut.stdout, 'filename'):
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)
359
360 self.output.write(line)
361
362 if self.print_fun and self.printfun_verbosity and priority >= self.printfun_verbosity:
363 self.use_print(line)
364
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
376 """Set a new print_fun."""
377 self.print_fun = True
378 self.thefun = [print_fun]
379
381 """Sets the verbosity level for *all* the output methods."""
382 self.verbosity = self.file_verbosity = self.printfun_verbosity = verbosity
383
385 return self.output.flush()
386
388 for line in inlines:
389 self.write(line)
390
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
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')