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