Output stream wrapper; possibly useful for debugging code with print statements.
When write() is called, it makes a note of the calling frame. The indentation level is equal to the number of frames in the call stack which have been previously noted. See example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | import sys
class AutoIndent(object):
def __init__(self, stream):
self.stream = stream
self.offset = 0
self.frame_cache = {}
def indent_level(self):
i = 0
base = sys._getframe(2)
f = base.f_back
while f:
if id(f) in self.frame_cache:
i += 1
f = f.f_back
if i == 0:
# clear out the frame cache
self.frame_cache = {id(base): True}
else:
self.frame_cache[id(base)] = True
return i
def write(self, stuff):
indentation = ' ' * self.indent_level()
def indent(l):
if l:
return indentation + l
else:
return l
stuff = '\n'.join([indent(line) for line in stuff.split('\n')])
self.stream.write(stuff)
--------------------------------------------------------------------------
>>> # Example usage
>>>
>>> def f(x):
... print "f(%s)" % x
... if x == 0:
... return 0
... elif x == 1:
... return 1
... else:
... return f(x-1) + f(x-2)
>>>
>>> import sys
>>> sys.stdout = AutoIndent(sys.stdout)
>>>
>>> f(6)
f(6)
f(5)
f(4)
f(3)
f(2)
f(1)
f(0)
f(1)
f(2)
f(1)
f(0)
f(3)
f(2)
f(1)
f(0)
f(1)
f(4)
f(3)
f(2)
f(1)
f(0)
f(1)
f(2)
f(1)
f(0)
8
>>>
|
Tags: debugging
Pretty cool. Neat :) This would be a useful feature to the logging module.
Problems using this in a logging handler. I tried
and the result was buggy. A function would get one indentation level the first time, and then a deeper level for every subsequent call. (Write me if you want the code.)
For example,
Produced
I fixed this by using
which produced
Unfortunately, using your class directly with my change, the output is completely unindented, while your version works perfectly.
I don't know what's going on, but getting this to work with logging is not trivial.
My version works consistently. I had subtracted too much. Normally, the stack is not 9 deep! This works really well in general:
Your version is definitely buggy when used inside a logging.Handler(), and that is a pretty big problem.
Better way to indent logging output. Another (better?) way is:
With %(indent)s in the format string, this produces
But then a regular formatter would raise an exception if given %(indent)s in the format string. So I'm not sure of the best way.
How do we discover the baseline stack depth? There has to be a way to find the baseline indentation level, rather than hard-coding it. For example, in ipython the stack is 8 deep in the shell alone!
Aside from that, this solution is pretty good. I am going to try to get the function name from the call stack too, and stick that into a generally useful logging.Formatter, which might be a good cookbook entry by itself.
base frame depth. I hadn't tried it in conjunction with the logging module... It may be that adding one to the argument to sys._getframe will solve the problem, but I'm not sure. I will look into it.
As for the base stack depth-- the way I tried to avoid that was by only counting frames which write() had been called from, instead of counting all frames. Otherwise the indentation gets insane.
Just what I wanted but I simplified slightly.