home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyth_os2.zip / python-1.0.2 / Lib / bdb.py < prev    next >
Text File  |  1994-03-15  |  8KB  |  306 lines

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