home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / python / Lib / bdb.py < prev    next >
Encoding:
Python Source  |  1994-04-01  |  8.1 KB  |  307 lines  |  [TEXT/R*ch]

  1. # A generic Python debugger base class.
  2. # This class takes care of details of the trace facility;
  3. # a derived class should implement user interaction.
  4. # There are two debuggers based upon this:
  5. # 'pdb', a text-oriented debugger not unlike dbx or gdb;
  6. # and 'wdb', a window-oriented debugger.
  7. # And of course... you can roll your own!
  8.  
  9. import sys
  10.  
  11. BdbQuit = 'bdb.BdbQuit' # Exception to give up completely
  12.  
  13.  
  14. class Bdb: # Basic Debugger
  15.     
  16.     def __init__(self):
  17.         self.breaks = {}
  18.     
  19.     def reset(self):
  20.         self.botframe = None
  21.         self.stopframe = None
  22.         self.returnframe = None
  23.         self.quitting = 0
  24.     
  25.     def trace_dispatch(self, frame, event, arg):
  26.         if self.quitting:
  27.             return # None
  28.         if event == 'line':
  29.             return self.dispatch_line(frame)
  30.         if event == 'call':
  31.             return self.dispatch_call(frame, arg)
  32.         if event == 'return':
  33.             return self.dispatch_return(frame, arg)
  34.         if event == 'exception':
  35.             return self.dispatch_exception(frame, arg)
  36.         print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
  37.         return self.trace_dispatch
  38.     
  39.     def dispatch_line(self, frame):
  40.         if self.stop_here(frame) or self.break_here(frame):
  41.             self.user_line(frame)
  42.             if self.quitting: raise BdbQuit
  43.         return self.trace_dispatch
  44.     
  45.     def dispatch_call(self, frame, arg):
  46.         frame.f_locals['__args__'] = arg
  47.         if self.botframe is None:
  48.             # First call of dispatch since reset()
  49.             self.botframe = frame
  50.             return self.trace_dispatch
  51.         if not (self.stop_here(frame) or self.break_anywhere(frame)):
  52.             # No need to trace this function
  53.             return # None
  54.         self.user_call(frame, arg)
  55.         if self.quitting: raise BdbQuit
  56.         return self.trace_dispatch
  57.     
  58.     def dispatch_return(self, frame, arg):
  59.         if self.stop_here(frame) or frame == self.returnframe:
  60.             self.user_return(frame, arg)
  61.             if self.quitting: raise BdbQuit
  62.     
  63.     def dispatch_exception(self, frame, arg):
  64.         if self.stop_here(frame):
  65.             self.user_exception(frame, arg)
  66.             if self.quitting: raise BdbQuit
  67.         return self.trace_dispatch
  68.     
  69.     # Normally derived classes don't override the following
  70.     # methods, but they may if they want to redefine the
  71.     # definition of stopping and breakpoints.
  72.     
  73.     def stop_here(self, frame):
  74.         if self.stopframe is None:
  75.             return 1
  76.         if frame is self.stopframe:
  77.             return 1
  78.         while frame is not None and frame is not self.stopframe:
  79.             if frame is self.botframe:
  80.                 return 1
  81.             frame = frame.f_back
  82.         return 0
  83.     
  84.     def break_here(self, frame):
  85.         if not self.breaks.has_key(frame.f_code.co_filename):
  86.             return 0
  87.         if not frame.f_lineno in \
  88.                 self.breaks[frame.f_code.co_filename]:
  89.             return 0
  90.         return 1
  91.     
  92.     def break_anywhere(self, frame):
  93.         return self.breaks.has_key(frame.f_code.co_filename)
  94.     
  95.     # Derived classes should override the user_* methods
  96.     # to gain control.
  97.     
  98.     def user_call(self, frame, argument_list):
  99.         # This method is called when there is the remote possibility
  100.         # that we ever need to stop in this function
  101.         pass
  102.     
  103.     def user_line(self, frame):
  104.         # This method is called when we stop or break at this line
  105.         pass
  106.     
  107.     def user_return(self, frame, return_value):
  108.         # This method is called when a return trap is set here
  109.         pass
  110.     
  111.     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
  112.         # This method is called if an exception occurs,
  113.         # but only if we are to stop at or just below this level
  114.         pass
  115.     
  116.     # Derived classes and clients can call the following methods
  117.     # to affect the stepping state.
  118.     
  119.     def set_step(self):
  120.         # Stop after one line of code
  121.         self.stopframe = None
  122.         self.returnframe = None
  123.         self.quitting = 0
  124.     
  125.     def set_next(self, frame):
  126.         # Stop on the next line in or below the given frame
  127.         self.stopframe = frame
  128.         self.returnframe = None
  129.         self.quitting = 0
  130.     
  131.     def set_return(self, frame):
  132.         # Stop when returning from the given frame
  133.         self.stopframe = frame.f_back
  134.         self.returnframe = frame
  135.         self.quitting = 0
  136.     
  137.     def set_continue(self):
  138.         # Don't stop except at breakpoints or when finished
  139.         self.stopframe = self.botframe
  140.         self.returnframe = None
  141.         self.quitting = 0
  142.     
  143.     def set_quit(self):
  144.         self.stopframe = self.botframe
  145.         self.returnframe = None
  146.         self.quitting = 1
  147.         sys.settrace(None)
  148.     
  149.     # Derived classes and clients can call the following methods
  150.     # to manipulate breakpoints.  These methods return an
  151.     # error message is something went wrong, None if all is well.
  152.     # Call self.get_*break*() to see the breakpoints.
  153.     
  154.     def set_break(self, filename, lineno):
  155.         import linecache # Import as late as possible
  156.         line = linecache.getline(filename, lineno)
  157.         if not line:
  158.             return 'That line does not exist!'
  159.         if not self.breaks.has_key(filename):
  160.             self.breaks[filename] = []
  161.         list = self.breaks[filename]
  162.         if lineno in list:
  163.             return 'There is already a breakpoint there!'
  164.         list.append(lineno)
  165.     
  166.     def clear_break(self, filename, lineno):
  167.         if not self.breaks.has_key(filename):
  168.             return 'There are no breakpoints in that file!'
  169.         if lineno not in self.breaks[filename]:
  170.             return 'There is no breakpoint there!'
  171.         self.breaks[filename].remove(lineno)
  172.         if not self.breaks[filename]:
  173.             del self.breaks[filename]
  174.     
  175.     def clear_all_file_breaks(self, filename):
  176.         if not self.breaks.has_key(filename):
  177.             return 'There are no breakpoints in that file!'
  178.         del self.breaks[filename]
  179.     
  180.     def clear_all_breaks(self, filename, lineno):
  181.         if not self.breaks:
  182.             return 'There are no breakpoints!'
  183.         self.breaks = {}
  184.     
  185.     def get_break(self, filename, lineno):
  186.         return self.breaks.has_key(filename) and \
  187.             lineno in self.breaks[filename]
  188.     
  189.     def get_file_breaks(self, filename):
  190.         if self.breaks.has_key(filename):
  191.             return self.breaks[filename]
  192.         else:
  193.             return []
  194.     
  195.     def get_all_breaks(self):
  196.         return self.breaks
  197.     
  198.     # Derived classes and clients can call the following method
  199.     # to get a data structure representing a stack trace.
  200.     
  201.     def get_stack(self, f, t):
  202.         stack = []
  203.         if t and t.tb_frame is f:
  204.             t = t.tb_next
  205.         while f is not None:
  206.             stack.append((f, f.f_lineno))
  207.             if f is self.botframe:
  208.                 break
  209.             f = f.f_back
  210.         stack.reverse()
  211.         i = max(0, len(stack) - 1)
  212.         while t is not None:
  213.             stack.append((t.tb_frame, t.tb_lineno))
  214.             t = t.tb_next
  215.         return stack, i
  216.     
  217.     # 
  218.     
  219.     def format_stack_entry(self, frame_lineno):
  220.         import codehack, linecache, repr, string
  221.         frame, lineno = frame_lineno
  222.         filename = frame.f_code.co_filename
  223.         s = filename + '(' + `lineno` + ')'
  224.         s = s + codehack.getcodename(frame.f_code)
  225.         if frame.f_locals.has_key('__args__'):
  226.             args = frame.f_locals['__args__']
  227.             if args is not None:
  228.                 s = s + repr.repr(args)
  229.         if frame.f_locals.has_key('__return__'):
  230.             rv = frame.f_locals['__return__']
  231.             s = s + '->'
  232.             s = s + repr.repr(rv)
  233.         line = linecache.getline(filename, lineno)
  234.         if line: s = s + ': ' + string.strip(line)
  235.         return s
  236.     
  237.     # The following two methods can be called by clients to use
  238.     # a debugger to debug a statement, given as a string.
  239.     
  240.     def run(self, cmd):
  241.         import __main__
  242.         dict = __main__.__dict__
  243.         self.runctx(cmd, dict, dict)
  244.     
  245.     def runctx(self, cmd, globals, locals):
  246.         self.reset()
  247.         sys.settrace(self.trace_dispatch)
  248.         try:
  249.             try:
  250.                 exec(cmd + '\n', globals, locals)
  251.             except BdbQuit:
  252.                 pass
  253.         finally:
  254.             self.quitting = 1
  255.             sys.settrace(None)
  256.  
  257.     # This method is more useful to debug a single function call.
  258.  
  259.     def runcall(self, func, *args):
  260.         self.reset()
  261.         sys.settrace(self.trace_dispatch)
  262.         try:
  263.             try:
  264.                 apply(func, args)
  265.             except BdbQuit:
  266.                 pass
  267.         finally:
  268.             self.quitting = 1
  269.             sys.settrace(None)
  270.  
  271.  
  272. # -------------------- testing --------------------
  273.  
  274. class Tdb(Bdb):
  275.     def user_call(self, frame, args):
  276.         import codehack
  277.         name = codehack.getcodename(frame.f_code)
  278.         if not name: name = '???'
  279.         print '+++ call', name, args
  280.     def user_line(self, frame):
  281.         import linecache, string, codehack
  282.         name = codehack.getcodename(frame.f_code)
  283.         if not name: name = '???'
  284.         fn = frame.f_code.co_filename
  285.         line = linecache.getline(fn, frame.f_lineno)
  286.         print '+++', fn, frame.f_lineno, name, ':', string.strip(line)
  287.     def user_return(self, frame, retval):
  288.         print '+++ return', retval
  289.     def user_exception(self, frame, exc_stuff):
  290.         print '+++ exception', exc_stuff
  291.         self.set_continue()
  292.  
  293. def foo(n):
  294.     print 'foo(', n, ')'
  295.     x = bar(n*10)
  296.     print 'bar returned', x
  297.  
  298. def bar(a):
  299.     print 'bar(', a, ')'
  300.     return a/2
  301.  
  302. def test():
  303.     import linecache
  304.     linecache.checkcache()
  305.     t = Tdb()
  306.     t.run('import bdb; bdb.foo(10)')
  307.