1
2
3
4
5
6
7
8
9
10
11
12 """
13 Simple debug module.
14
15 Used for fast debugging. You may set debug output into two different
16 streams: L{outstream} and L{fstream}. Preferred assignment for streams is:
17 - outstream - any console output (by default I{stderr}.
18 - fstream - any file output (default is "dbg.log" file into "~/.XPyLIB/logs"),
19 where "~" is a user home directory. A dir mode is 0660. If path is
20 not exist then created automaticaly.
21 Function for file output starts with B{f} letter.
22 Initialization of streams (open file) is doing only if calling writing
23 function and stream is not initialized. Before start debug output you can
24 initialize streams you own objects which support B{write} method.
25 Then program exit autocalling L{shutdown} function which close L{fstream}
26 if it was opened. If in program no debug output then streams are not
27 created and file are not opened.
28
29 Also you may use indentation level by calling L{lvlup}(), L{lvldown}() and
30 decorators L{level}, L{wlevel} and L{fwlevel}.
31 Indentation level used only in next functions: L{fwriteln}()
32 and L{writeln}(). For tune indentation use L{levelbodychar},
33 L{levelendchar}, L{levelwidth}.
34
35
36 Debug output example, see L{_debug})::
37
38 *** DEBUG START ***
39
40 >456
41 ..>tst1:test
42 >CALL tst2()
43 ..>tst2 start
44 ..>tst1:from tst2
45 ..>CALL tst3()
46 ....>tst3
47 ..>RET from tst3()
48 ..>tst2 end
49 >RET from tst2()
50 >CALL tst2()
51 ..>tst2 start
52 ..>tst1:from tst2
53 ..>CALL tst3()
54 ....>tst3
55 ..>RET from tst3()
56 ..>tst2 end
57 >RET from tst2()
58
59
60 *** DEBUG END ***
61
62
63 @var outstream: Output stream, default is I{sys.stdout}.
64 @type outstream: stream(file-like object).
65
66 @var fstream: Output stream, default is "dbg.log" file into "~/.XPyLIB/logs"
67 directory or sys.stderr if file is not available. This stream autoclosed
68 then exit from application (used atexit module).
69 @type fstream: stream(file-like object).
70 @var levelbodychar: Character that fill indentation spaces.
71 @type levelbodychar: str
72 @var levelendchar: Character that insert before string output but after
73 level indentation.
74 @type levelendchar: str
75 @var levelwidth: Number of indentation characters in one indentation level.
76 @type levelwidth: int
77 @var levelerr: Error message then level indentation is invalid thrn return from
78 wrapped function.
79 @type levelerr: int
80 @var _level: Indentation level.
81 @type _level: int
82
83 @var descfuncall: Function call description.
84 @type descfuncall: str
85 @var descfunret: Function return description.
86 @type descfunret: str
87
88 @var desceval: String that prepend to eval expression.
89 @type desceval: str
90 @var descevalrez: String that prepend to eval resultat.
91 @type descevalrez: str
92 @var descevalexc: String that prepend to eval exception.
93 @type descevalexc: str
94 @var descevalln: String that added at end of eval output.
95 @type descevalln: str
96 """
97
98 import sys
99 import atexit
100 import os
101 from functools import wraps
102
103 outstream = None
104 fstream = None
105
106 levelbodychar = '.'
107 levelendchar = '>'
108 levelwidth = 2
109 levelerr = '*** LEVEL ERROR = '
110 descfuncall = 'CALL '
111 descfunret = 'RET from '
112 desceval = 'EVAL: '
113 descevalrez = ' ER='
114 descevalexc = ' EXCEPTION:'
115 descevalln = ' '
116
117 _level = 0
118
120 """
121 Increase indentation level.
122 """
123 global _level
124 _level+=1
125
134
136 """
137 Reset indentation level to "0".
138 """
139 global _level
140 _level = 0
141
143 """
144 Auto level decorator for functions.
145
146 Call L{lvlup}() befor function call and L{lvldown}() after
147 wrapped function return.
148 @param fun: Wrapped function.
149 @type fun: func
150 """
151 @wraps(fun)
152 def wrapper(*args, **kwds):
153 global _level
154 l=_level
155 lvlup()
156 exc=False
157 try:
158 r = fun(*args, **kwds)
159 except Exception, e:
160 exc=e
161
162 _level=l
163 if exc:
164 raise exc
165 return r
166 return wrapper
167
169 """
170 Auto level and name decorator for functions.
171
172 Write first 'Call "fun.__name__"' using L{fwriteln}().
173 Also as L{level}().
174 If level error tested then write '*** DBG LEVEL ERROR = different'
175 where 'different' is level different.
176 Write last 'Return from "fun.__name__"' using L{fwriteln}().
177 @param fun: Wrapped function.
178 @type fun: func
179 """
180 @wraps(fun)
181 def wrapper(*args, **kwds):
182 global _level
183 fwriteln(descfuncall, fun.__name__, '()')
184 l=_level
185 lvlup()
186 exc=False
187 try:
188 r = fun(*args, **kwds)
189 except Exception, e:
190 exc=e
191 lvldown()
192 if l != _level:
193 d=_level-l
194 _level=l
195 fwriteln(levelerr, d)
196 fwriteln(descfunret, fun.__name__, '()')
197 if exc:
198 raise exc
199 return r
200 return wrapper
201
203 """
204 Auto level and name decorator for functions.
205
206 Write first 'Call "fun.__name__"' using L{writeln}().
207 Also as L{level}().
208 If level error tested then write '*** DBG LEVEL ERROR = different'
209 where 'different' is level different.
210 Write last 'Return from "fun.__name__"' using L{writeln}().
211 @param fun: Wrapped function.
212 @type fun: func
213 """
214 @wraps(fun)
215 def wrapper(*args, **kwds):
216 global _level
217 writeln(descfuncall, fun.__name__, '()')
218 l=_level
219 lvlup()
220 exc=False
221 try:
222 r = fun(*args, **kwds)
223 except Exception, e:
224 exc=e
225 lvldown()
226 if l != _level:
227 d=_level-l
228 _level=l
229 fwriteln(levelerr, d)
230 writeln(descfunret, fun.__name__, '()')
231 if exc:
232 raise exc
233 return r
234 return wrapper
235
249
258
260 """
261 Initialize file output stream.
262
263 Default is "dbg.log" file into "~/.XPyLIB/logs"
264 directory or sys.stderr if file is not available.
265 Where "~" is a user home directory. Path autocreate if need.
266 Mode of dirs is 0660.
267 """
268 global fstream
269 if not fstream:
270 try:
271 p=os.path.expanduser("~")
272 p=os.path.join(p, ".xpylib", "logs")
273 if not os.path.exists(p):
274 os.makedirs(p,0660)
275 p=os.path.join(p,'dbg.log')
276 fstream=open(p,'w',0)
277
278
279
280
281
282
283
284 except Exception, e:
285 fstream=sys.stderr
286 fstream.write('Can\'t open file for debug logging.\nerror:' + \
287 str(e) + '\n\n')
288 fstream.write('*** DEBUG START ***\n\n')
289
290
292 """
293 Write concatenated arguments into outstream.
294
295 @param arg: Object to output. All objects convert to string I{str()} function.
296 @type arg: *object
297 @return: None.
298 @rtype: None
299 """
300 fstream_init()
301 fstream.write( reduce(lambda a, b: a + str(b), arg, '') )
302
303
305 """
306 Write concatenated arguments into outstream.
307
308 @param arg: Object to output. All objects convert to string I{str()} function.
309 @type arg: *object
310 @return: None.
311 @rtype: None
312 """
313 outstream_init()
314 outstream.write( reduce(lambda a, b: a + str(b), arg, '') )
315
317 """
318 Write concatenated arguments into outstream and append new line ('\\n'). For
319 output used fun.
320
321 @param fun: Function for output (L{write} or L{fwrite} for example).
322 @type fun: func
323 @param arg: Object to output. All objects convert to string I{str()} function.
324 @type arg: *object
325 @return: None.
326 @rtype: None
327 @note: For work this function will be call L{fwrite} function.
328 """
329 fun((levelbodychar * levelwidth * _level) + levelendchar)
330 fun(*arg )
331 fun('\n' )
332
334 """
335 Write concatenated arguments into fstream and append new line ('\\n').
336
337 @param arg: Object to output. All objects convert to string I{str()} function.
338 @type arg: *object
339 @return: None.
340 @rtype: None
341 @note: For work this function will be used L{fwrite} function.
342 """
343 _writeln(fwrite, *arg)
344
346 """
347 Write concatenated arguments into outstream and append new line ('\\n').
348
349 @param arg: Object to output. All objects convert to string I{str()} function.
350 @type arg: *object
351 @return: None.
352 @rtype: None
353 @note: For work this function will be used L{write} function.
354 """
355 _writeln(write, *arg)
356
358 """
359 As L{fwrite} but return arguments tuple and for output use fun.
360
361 @param fun: Function for output (L{write} or L{fwrite} for example).
362 @type fun: func
363 @param arg: Object to output. All objects convert to string I{str()} function.
364 @type arg: *object
365 @return: Arguments tuple.
366 @rtype: tuple
367 """
368 fun(*arg)
369 return arg
370
372 """
373 As L{fwrite} but return arguments tuple.
374
375 @param arg: Object to output. All objects convert to string I{str()} function.
376 @type arg: *object
377 @return: Arguments tuple.
378 @rtype: tuple
379 """
380 return _rwrite(fwrite, *arg)
381
383 """
384 As L{write} but return arguments tuple.
385
386 @param arg: Object to output. All objects convert to string I{str()} function.
387 @type arg: *object
388 @return: Arguments tuple.
389 @rtype: tuple
390 """
391 return _rwrite(write, *arg)
392
393 -def _eval(fun, expr, g=globals(), l=locals()):
394 """
395 Write expr, evaluate it and write result to output using fun.
396
397 Write algorithm:
398 - write L{desceval} and expr in quotes.
399 - evaluate expr using eval.
400 - if exception occur then write L{descevalexc} and quoted exception.
401 - if no error then write L{descevalrez} and quoted result.
402 - write L{descevalln}.
403
404 Then write this function you must get globals() and locals() if you using
405 global or local scope variables into exprwssion.
406
407 For example see L{weval}.
408
409 @param fun: Function for output (L{write} or L{fwrite} for example).
410 @type fun: func
411 @param expr: Expression string.
412 @type expr: str
413 @param g: Global variables.
414 @type g: dict = globals()
415 @param l: Local variables.
416 @type l: dict = locals()
417 @return: Result of evaluation or Exception.
418 @rtype: object or Exception
419 """
420 fun(desceval, '"', expr, '"')
421 exc = False
422 try:
423 er = eval(expr, g, l)
424 except Exception, e:
425 exc = True
426 er = e
427 if exc:
428 fun(descevalexc, '"', er, '"')
429 else:
430 fun(descevalrez, '"', er, '"')
431 if descevalln:
432 fun(descevalln)
433 return er
434
435 -def weval(expr, g=globals(), l=locals()):
436 """
437 Write expr, evaluate it and write result to outstream.
438
439 Write algorithm:
440 - write L{desceval} and expr in quotes.
441 - evaluate expr using eval.
442 - if exception occur then write L{descevalexc} and quoted exception.
443 - if no error then write L{descevalrez} and quoted result.
444 - write L{descevalln}.
445
446 Then write this function you must get globals() and locals() if you using
447 global or local scope variables into exprwssion.
448
449 For example: C{ dbg.weval('dir(a)', globals(), locals()) }
450
451 @param expr: Expression string.
452 @type expr: str
453 @param g: Global variables.
454 @type g: dict = globals()
455 @param l: Local variables.
456 @type l: dict = locals()
457 @return: Result of evaluation or Exception.
458 @rtype: object or Exception
459 """
460 return _eval(writeln, expr,g, l)
461
462 -def fweval(expr, g=globals(), l=locals()):
463 """
464 Write expr, evaluate it and write result to fstream.
465
466 Write algorithm:
467 - write L{desceval} and expr in quotes.
468 - evaluate expr using eval.
469 - if exception occur then write L{descevalexc} and quoted exception.
470 - if no error then write L{descevalrez} and quoted result.
471 - write L{descevalln}.
472
473 Then write this function you must get globals() and locals() if you using
474 global or local scope variables into exprwssion.
475
476 For example: C{ dbg.feval('dir(a)', globals(), locals()) }
477
478 @param expr: Expression string.
479 @type expr: str
480 @param g: Global variables.
481 @type g: dict = globals()
482 @param l: Local variables.
483 @type l: dict = locals()
484 @return: Result of evaluation or Exception.
485 @rtype: object or Exception
486 """
487 return _eval(fwriteln, expr, g, l)
488
489
490
491
492
493
494 atexit.register(shutdown)
495
496
497
498
499
501 """
502 Test function.
503
504 Output to outstream is a next::
505
506 *** DEBUG START ***
507
508 >456
509 ..>tst1:test
510 >CALL tst2()
511 ..>tst2 start
512 ..>tst1:from tst2
513 ..>CALL tst3()
514 ....>tst3
515 ..>RET from tst3()
516 ..>tst2 end
517 >RET from tst2()
518 >CALL tst2()
519 ..>tst2 start
520 ..>tst1:from tst2
521 ..>CALL tst3()
522 ....>tst3
523 ..>RET from tst3()
524 ..>tst2 end
525 >RET from tst2()
526 arg1arg2>
527 rwrite return: ('arg1', 'arg2')
528 >EVAL: "str(5+6)"
529 > ER="11"
530 >
531 >
532 weval return: 11
533 >CALL tst4()
534 >RET from tst4()
535 >Exception OK
536
537
538 *** DEBUG END ***
539
540 """
541 def tst1(a):
542 writeln('tst1:', a)
543
544 @wlevel
545 def tst2():
546 writeln('tst2 start')
547 tst1('from tst2')
548 tst3()
549 writeln('tst2 end')
550
551 @wlevel
552 def tst3():
553 writeln('tst3')
554 print tst3.func_name
555
556 @wlevel
557 def tst4():
558 raise AttributeError
559
560 writeln(456)
561 fwriteln(456)
562 lvlup()
563 tst1('test')
564 lvldown()
565 tst2()
566 lvldown()
567 lvldown()
568 lvldown()
569 tst2()
570 writeln('\nrwrite return: %s' % (rwrite('arg1', 'arg2'), ) )
571 writeln('\nweval return: %s' % (weval('str(5+6)'), ) )
572 try:
573 tst4()
574 except AttributeError:
575 writeln('Exception OK')
576 else:
577 writeln('Exception fails')
578
579 pass
580
581
582
583 if __name__=='__main__':
584 _debug()
585