Controlling Python
Michael P. Reilly
arcege at shore.net
Wed Aug 18 08:02:13 EDT 1999
nriedlin at my-deja.com wrote:
:> You can use a callback which you can register with the
:> sys.settrace() function. A less fine-grained hook is available
:> through sys.setprofile(). See the docs for more infos.
:>
: You are correct, but isn't it true, that the callback is only called
: every function call? I would like to interfere every line (and so being
: able to trace my scripts). Please correct me if I'm wrong.
: TIA
The settrace callback is envoked under four events: call, line, return,
and exception. Below is a subsystem I wrote to help debugging. Note that
there is a known "bug" in that call events do not return the arguments as
documented.
For more info, read:
Python 1.5.2 Library Reference Manual 9.2 "How It Works"
http://www.python.org/doc/current/lib/node201.html
-Arcege
#!/usr/local/bin/python
import sys, traceback
import linecache
class Tracer:
output = sys.stderr
linecache = {}
indent_value = 0
def __init__(self, file=None):
if file is None:
pass
elif type(file) is type(''):
self.output = open(file, 'w')
else:
self.output = file
def write(self, *args):
apply(self.output.write, args)
self.output.flush()
def load_file(self, filename):
if not self.linecache.has_key(filename):
try:
self.linecache[filename] = open(filename).readlines()
except:
self.linecache[filename] = []
def lineno(self, filename, line):
return linecache.getline(filename, line)
self.load_file(filename)
if line < len(self.linecache[filename]):
return self.linecache[filename][line]
return None
def indent(self):
self.indent_value = self.indent_value + 1
def outdent(self):
self.indent_value = self.indent_value - 1
def output_indent(self):
self.write(' ' * self.indent_value)
def __call__(self, frame, event, arg):
method_name = 'type_%s' % event
getattr(self, method_name)(frame, arg)
return self
def type_call(self, frame, arg):
self.output_indent()
self.indent()
self.write('Function call: %s' % frame.f_code.co_name)
self.write('(')
l = arg and len(arg) or 0
for i in range(l):
self.write(repr(arg))
if i < l-1:
self.write(', ')
self.write(')')
self.write('\n')
return self
def type_line(self, frame, arg):
code = frame.f_code
file = code.co_filename
line = frame.f_lineno
self.output_indent()
self.write('Line number %d in %s (%s)\n ->%s' %
(line, file, code.co_name, self.lineno(file, line)))
return self
def type_return(self, frame, arg):
self.outdent()
self.output_indent()
self.write('Function %s returning %s\n' %
(frame.f_code.co_name, repr(arg)))
def type_exception(self, frame, arg):
code = frame.f_code
funcname = code.co_name
exc, value, traceback = arg
t = type(exc)
if t is type(''):
name = exc
elif t is type(self):
name = exc.__class__.__name__
elif t is type(self.__class__):
name = exc.__name__
else:
name = repr(exc)
self.output_indent()
self.write('Exception %s raised in %s\n' % (name, funcname))
self.outdent()
return self.inside_exception
def inside_exception(self, frame, event, arg):
self.write('Exception: %s\n' % event)
return self.inside_exception
def settrace(tracer):
# Start debugging from here
try:
raise SystemError
except:
if hasattr(sys, 'exc_info'):
frame = sys.exc_info()[2].tb_frame.f_back
else:
frame = sys.exc_traceback.tb_frame.f_back
while frame:
frame.f_trace = tracer
frame = frame.f_back
sys.settrace(tracer)
def unsettrace():
settrace(None)
class _Num:
def __init__(self, value=None):
if value is None:
self.value = self.start
else:
self.value = value
def __str__(self):
return str(self.value)
class _Sigma(_Num):
start = 0
def __add__(self, other):
self.value = self.value + other
return self
class _Pi(_Num):
start = long(1)
def __mul__(self, other):
self.value = self.value * other
return self
def _test(count):
sum = _Sigma(); aggr = _Pi()
for i in xrange(count):
j = i + 1
sum = sum + j
aggr = aggr * j
print 'Sigma(%d)' % count, sum
print 'Pi(%d)' % count, aggr
if __name__ == '__main__':
tracer = Tracer()
settrace(tracer)
_test(100)
More information about the Python-list
mailing list