home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pytho152.zip / emx / lib / python1.5 / bdb.py < prev    next >
Text File  |  2000-08-10  |  15KB  |  556 lines

  1. # Debugger basics
  2.  
  3. import sys
  4. import types
  5.  
  6. BdbQuit = 'bdb.BdbQuit' # Exception to give up completely
  7.  
  8.  
  9. class Bdb:
  10.     
  11.     """Generic Python debugger base class.
  12.  
  13.     This class takes care of details of the trace facility;
  14.     a derived class should implement user interaction.
  15.     The standard debugger class (pdb.Pdb) is an example.
  16.     """
  17.  
  18.     def __init__(self):
  19.         self.breaks = {}
  20.         # We want to have a method self.canonic() which
  21.         # canonicalizes filenames before comparing them
  22.         # but we want the default to be a very fast no-op.
  23.         # Solution: the built-in str function.
  24.         if not hasattr(self, "canonic"):
  25.             self.canonic = str
  26.     
  27.     def reset(self):
  28.         import linecache
  29.         linecache.checkcache()
  30.         self.botframe = None
  31.         self.stopframe = None
  32.         self.returnframe = None
  33.         self.quitting = 0
  34.     
  35.     def trace_dispatch(self, frame, event, arg):
  36.         if self.quitting:
  37.             return # None
  38.         if event == 'line':
  39.             return self.dispatch_line(frame)
  40.         if event == 'call':
  41.             return self.dispatch_call(frame, arg)
  42.         if event == 'return':
  43.             return self.dispatch_return(frame, arg)
  44.         if event == 'exception':
  45.             return self.dispatch_exception(frame, arg)
  46.         print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
  47.         return self.trace_dispatch
  48.     
  49.     def dispatch_line(self, frame):
  50.         if self.stop_here(frame) or self.break_here(frame):
  51.             self.user_line(frame)
  52.             if self.quitting: raise BdbQuit
  53.         return self.trace_dispatch
  54.     
  55.     def dispatch_call(self, frame, arg):
  56.         # XXX 'arg' is no longer used
  57.         if self.botframe is None:
  58.             # First call of dispatch since reset()
  59.             self.botframe = frame
  60.             return self.trace_dispatch
  61.         if not (self.stop_here(frame) or self.break_anywhere(frame)):
  62.             # No need to trace this function
  63.             return # None
  64.         self.user_call(frame, arg)
  65.         if self.quitting: raise BdbQuit
  66.         return self.trace_dispatch
  67.     
  68.     def dispatch_return(self, frame, arg):
  69.         if self.stop_here(frame) or frame == self.returnframe:
  70.             self.user_return(frame, arg)
  71.             if self.quitting: raise BdbQuit
  72.     
  73.     def dispatch_exception(self, frame, arg):
  74.         if self.stop_here(frame):
  75.             self.user_exception(frame, arg)
  76.             if self.quitting: raise BdbQuit
  77.         return self.trace_dispatch
  78.     
  79.     # Normally derived classes don't override the following
  80.     # methods, but they may if they want to redefine the
  81.     # definition of stopping and breakpoints.
  82.     
  83.     def stop_here(self, frame):
  84.         if self.stopframe is None:
  85.             return 1
  86.         if frame is self.stopframe:
  87.             return 1
  88.         while frame is not None and frame is not self.stopframe:
  89.             if frame is self.botframe:
  90.                 return 1
  91.             frame = frame.f_back
  92.         return 0
  93.  
  94.     def break_here(self, frame):
  95.         filename = self.canonic(frame.f_code.co_filename)
  96.         if not self.breaks.has_key(filename):
  97.             return 0
  98.         lineno = frame.f_lineno
  99.         if not lineno in self.breaks[filename]:
  100.             return 0
  101.         # flag says ok to delete temp. bp
  102.         (bp, flag) = effective(filename, lineno, frame)
  103.         if bp:
  104.             self.currentbp = bp.number
  105.             if (flag and bp.temporary):
  106.                 self.do_clear(str(bp.number))
  107.             return 1
  108.         else:
  109.             return 0
  110.     
  111.     def break_anywhere(self, frame):
  112.         return self.breaks.has_key(
  113.             self.canonic(frame.f_code.co_filename))
  114.     
  115.     # Derived classes should override the user_* methods
  116.     # to gain control.
  117.     
  118.     def user_call(self, frame, argument_list):
  119.         # This method is called when there is the remote possibility
  120.         # that we ever need to stop in this function
  121.         pass
  122.     
  123.     def user_line(self, frame):
  124.         # This method is called when we stop or break at this line
  125.         pass
  126.     
  127.     def user_return(self, frame, return_value):
  128.         # This method is called when a return trap is set here
  129.         pass
  130.     
  131.     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
  132.         # This method is called if an exception occurs,
  133.         # but only if we are to stop at or just below this level
  134.         pass
  135.     
  136.     # Derived classes and clients can call the following methods
  137.     # to affect the stepping state.
  138.     
  139.     def set_step(self):
  140.         # Stop after one line of code
  141.         self.stopframe = None
  142.         self.returnframe = None
  143.         self.quitting = 0
  144.     
  145.     def set_next(self, frame):
  146.         # Stop on the next line in or below the given frame
  147.         self.stopframe = frame
  148.         self.returnframe = None
  149.         self.quitting = 0
  150.     
  151.     def set_return(self, frame):
  152.         # Stop when returning from the given frame
  153.         self.stopframe = frame.f_back
  154.         self.returnframe = frame
  155.         self.quitting = 0
  156.     
  157.     def set_trace(self):
  158.         # Start debugging from here
  159.         try:
  160.             1 + ''
  161.         except:
  162.             frame = sys.exc_info()[2].tb_frame.f_back
  163.         self.reset()
  164.         while frame:
  165.             frame.f_trace = self.trace_dispatch
  166.             self.botframe = frame
  167.             frame = frame.f_back
  168.         self.set_step()
  169.         sys.settrace(self.trace_dispatch)
  170.  
  171.     def set_continue(self):
  172.         # Don't stop except at breakpoints or when finished
  173.         self.stopframe = self.botframe
  174.         self.returnframe = None
  175.         self.quitting = 0
  176.         if not self.breaks:
  177.             # no breakpoints; run without debugger overhead
  178.             sys.settrace(None)
  179.             try:
  180.                 1 + ''    # raise an exception
  181.             except:
  182.                 frame = sys.exc_info()[2].tb_frame.f_back
  183.             while frame and frame is not self.botframe:
  184.                 del frame.f_trace
  185.                 frame = frame.f_back
  186.     
  187.     def set_quit(self):
  188.         self.stopframe = self.botframe
  189.         self.returnframe = None
  190.         self.quitting = 1
  191.         sys.settrace(None)
  192.     
  193.     # Derived classes and clients can call the following methods
  194.     # to manipulate breakpoints.  These methods return an
  195.     # error message is something went wrong, None if all is well.
  196.     # Set_break prints out the breakpoint line and file:lineno.
  197.     # Call self.get_*break*() to see the breakpoints or better
  198.     # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
  199.     
  200.     def set_break(self, filename, lineno, temporary=0, cond = None):
  201.         filename = self.canonic(filename)
  202.         import linecache # Import as late as possible
  203.         line = linecache.getline(filename, lineno)
  204.         if not line:
  205.             return 'Line %s:%d does not exist' % (filename,
  206.                                    lineno)
  207.         if not self.breaks.has_key(filename):
  208.             self.breaks[filename] = []
  209.         list = self.breaks[filename]
  210.         if not lineno in list:
  211.             list.append(lineno)
  212.         bp = Breakpoint(filename, lineno, temporary, cond)
  213.  
  214.     def clear_break(self, filename, lineno):
  215.         filename = self.canonic(filename)
  216.         if not self.breaks.has_key(filename):
  217.             return 'There are no breakpoints in %s' % filename
  218.         if lineno not in self.breaks[filename]:
  219.             return 'There is no breakpoint at %s:%d' % (filename,
  220.                                     lineno)
  221.         # If there's only one bp in the list for that file,line
  222.         # pair, then remove the breaks entry
  223.         for bp in Breakpoint.bplist[filename, lineno][:]:
  224.             bp.deleteMe()
  225.         if not Breakpoint.bplist.has_key((filename, lineno)):
  226.             self.breaks[filename].remove(lineno)
  227.         if not self.breaks[filename]:
  228.             del self.breaks[filename]
  229.     
  230.     def clear_bpbynumber(self, arg):
  231.         try:
  232.             number = int(arg)
  233.         except:
  234.             return 'Non-numeric breakpoint number (%s)' % arg
  235.         try:
  236.             bp = Breakpoint.bpbynumber[number]
  237.         except IndexError:
  238.             return 'Breakpoint number (%d) out of range' % number
  239.         if not bp:
  240.             return 'Breakpoint (%d) already deleted' % number
  241.         self.clear_break(bp.file, bp.line)
  242.  
  243.     def clear_all_file_breaks(self, filename):
  244.         filename = self.canonic(filename)
  245.         if not self.breaks.has_key(filename):
  246.             return 'There are no breakpoints in %s' % filename
  247.         for line in self.breaks[filename]:
  248.             blist = Breakpoint.bplist[filename, line]
  249.             for bp in blist:
  250.                 bp.deleteMe()
  251.         del self.breaks[filename]
  252.     
  253.     def clear_all_breaks(self):
  254.         if not self.breaks:
  255.             return 'There are no breakpoints'
  256.         for bp in Breakpoint.bpbynumber:
  257.             if bp:
  258.                 bp.deleteMe()
  259.         self.breaks = {}
  260.     
  261.     def get_break(self, filename, lineno):
  262.         filename = self.canonic(filename)
  263.         return self.breaks.has_key(filename) and \
  264.             lineno in self.breaks[filename]
  265.     
  266.     def get_breaks(self, filename, lineno):
  267.         filename = self.canonic(filename)
  268.         return self.breaks.has_key(filename) and \
  269.             lineno in self.breaks[filename] and \
  270.             Breakpoint.bplist[filename, lineno] or []
  271.     
  272.     def get_file_breaks(self, filename):
  273.         filename = self.canonic(filename)
  274.         if self.breaks.has_key(filename):
  275.             return self.breaks[filename]
  276.         else:
  277.             return []
  278.     
  279.     def get_all_breaks(self):
  280.         return self.breaks
  281.     
  282.     # Derived classes and clients can call the following method
  283.     # to get a data structure representing a stack trace.
  284.     
  285.     def get_stack(self, f, t):
  286.         stack = []
  287.         if t and t.tb_frame is f:
  288.             t = t.tb_next
  289.         while f is not None:
  290.             stack.append((f, f.f_lineno))
  291.             if f is self.botframe:
  292.                 break
  293.             f = f.f_back
  294.         stack.reverse()
  295.         i = max(0, len(stack) - 1)
  296.         while t is not None:
  297.             stack.append((t.tb_frame, t.tb_lineno))
  298.             t = t.tb_next
  299.         return stack, i
  300.     
  301.     # 
  302.     
  303.     def format_stack_entry(self, frame_lineno, lprefix=': '):
  304.         import linecache, repr, string
  305.         frame, lineno = frame_lineno
  306.         filename = self.canonic(frame.f_code.co_filename)
  307.         s = filename + '(' + `lineno` + ')'
  308.         if frame.f_code.co_name:
  309.             s = s + frame.f_code.co_name
  310.         else:
  311.             s = s + "<lambda>"
  312.         if frame.f_locals.has_key('__args__'):
  313.             args = frame.f_locals['__args__']
  314.         else:
  315.             args = None
  316.         if args:
  317.             s = s + repr.repr(args)
  318.         else:
  319.             s = s + '()'
  320.         if frame.f_locals.has_key('__return__'):
  321.             rv = frame.f_locals['__return__']
  322.             s = s + '->'
  323.             s = s + repr.repr(rv)
  324.         line = linecache.getline(filename, lineno)
  325.         if line: s = s + lprefix + string.strip(line)
  326.         return s
  327.     
  328.     # The following two methods can be called by clients to use
  329.     # a debugger to debug a statement, given as a string.
  330.     
  331.     def run(self, cmd, globals=None, locals=None):
  332.         if globals is None:
  333.             import __main__
  334.             globals = __main__.__dict__
  335.         if locals is None:
  336.             locals = globals
  337.         self.reset()
  338.         sys.settrace(self.trace_dispatch)
  339.         if type(cmd) <> types.CodeType:
  340.             cmd = cmd+'\n'
  341.         try:
  342.             try:
  343.                 exec cmd in globals, locals
  344.             except BdbQuit:
  345.                 pass
  346.         finally:
  347.             self.quitting = 1
  348.             sys.settrace(None)
  349.     
  350.     def runeval(self, expr, globals=None, locals=None):
  351.         if globals is None:
  352.             import __main__
  353.             globals = __main__.__dict__
  354.         if locals is None:
  355.             locals = globals
  356.         self.reset()
  357.         sys.settrace(self.trace_dispatch)
  358.         if type(expr) <> types.CodeType:
  359.             expr = expr+'\n'
  360.         try:
  361.             try:
  362.                 return eval(expr, globals, locals)
  363.             except BdbQuit:
  364.                 pass
  365.         finally:
  366.             self.quitting = 1
  367.             sys.settrace(None)
  368.  
  369.     def runctx(self, cmd, globals, locals):
  370.         # B/W compatibility
  371.         self.run(cmd, globals, locals)
  372.  
  373.     # This method is more useful to debug a single function call.
  374.  
  375.     def runcall(self, func, *args):
  376.         self.reset()
  377.         sys.settrace(self.trace_dispatch)
  378.         res = None
  379.         try:
  380.             try:
  381.                 res = apply(func, args)
  382.             except BdbQuit:
  383.                 pass
  384.         finally:
  385.             self.quitting = 1
  386.             sys.settrace(None)
  387.         return res
  388.  
  389.  
  390. def set_trace():
  391.     Bdb().set_trace()
  392.  
  393.  
  394. class Breakpoint:
  395.  
  396.     """Breakpoint class
  397.  
  398.     Implements temporary breakpoints, ignore counts, disabling and
  399.     (re)-enabling, and conditionals.
  400.  
  401.     Breakpoints are indexed by number through bpbynumber and by
  402.     the file,line tuple using bplist.  The former points to a
  403.     single instance of class Breakpoint.  The latter points to a
  404.     list of such instances since there may be more than one
  405.     breakpoint per line.
  406.  
  407.     """
  408.  
  409.     # XXX Keeping state in the class is a mistake -- this means
  410.     # you cannot have more than one active Bdb instance.
  411.  
  412.     next = 1        # Next bp to be assigned
  413.     bplist = {}        # indexed by (file, lineno) tuple
  414.     bpbynumber = [None]    # Each entry is None or an instance of Bpt
  415.                 # index 0 is unused, except for marking an
  416.                 # effective break .... see effective()
  417.  
  418.     def __init__(self, file, line, temporary=0, cond = None):
  419.         self.file = file    # This better be in canonical form!
  420.         self.line = line
  421.         self.temporary = temporary
  422.         self.cond = cond
  423.         self.enabled = 1
  424.         self.ignore = 0
  425.         self.hits = 0
  426.         self.number = Breakpoint.next
  427.         Breakpoint.next = Breakpoint.next + 1
  428.         # Build the two lists
  429.         self.bpbynumber.append(self)
  430.         if self.bplist.has_key((file, line)):
  431.             self.bplist[file, line].append(self)
  432.         else:
  433.             self.bplist[file, line] = [self]
  434.  
  435.         
  436.     def deleteMe(self):
  437.         index = (self.file, self.line)
  438.         self.bpbynumber[self.number] = None   # No longer in list
  439.         self.bplist[index].remove(self)
  440.         if not self.bplist[index]:
  441.             # No more bp for this f:l combo
  442.             del self.bplist[index]
  443.  
  444.     def enable(self):
  445.         self.enabled = 1
  446.  
  447.     def disable(self):
  448.         self.enabled = 0
  449.  
  450.     def bpprint(self):
  451.         if self.temporary:
  452.            disp = 'del  '
  453.         else:
  454.            disp = 'keep '
  455.         if self.enabled:
  456.            disp = disp + 'yes'
  457.         else:
  458.            disp = disp + 'no '
  459.         print '%-4dbreakpoint     %s at %s:%d' % (self.number, disp,
  460.                              self.file, self.line)
  461.         if self.cond:
  462.             print '\tstop only if %s' % (self.cond,)
  463.         if self.ignore:
  464.             print '\tignore next %d hits' % (self.ignore)
  465.         if (self.hits):
  466.             if (self.hits > 1): ss = 's'
  467.             else: ss = ''
  468.             print ('\tbreakpoint already hit %d time%s' %
  469.                    (self.hits, ss))
  470.  
  471. # -----------end of Breakpoint class----------
  472.  
  473. # Determines if there is an effective (active) breakpoint at this
  474. # line of code.  Returns breakpoint number or 0 if none
  475. def effective(file, line, frame):
  476.     """Determine which breakpoint for this file:line is to be acted upon.
  477.  
  478.     Called only if we know there is a bpt at this
  479.     location.  Returns breakpoint that was triggered and a flag
  480.     that indicates if it is ok to delete a temporary bp.
  481.  
  482.     """
  483.     possibles = Breakpoint.bplist[file,line]
  484.     for i in range(0, len(possibles)):
  485.         b = possibles[i]
  486.         if b.enabled == 0:
  487.             continue
  488.         # Count every hit when bp is enabled
  489.         b.hits = b.hits + 1
  490.         if not b.cond:
  491.             # If unconditional, and ignoring,
  492.             # go on to next, else break
  493.             if b.ignore > 0:
  494.                 b.ignore = b.ignore -1
  495.                 continue
  496.             else:
  497.                 # breakpoint and marker that's ok
  498.                 # to delete if temporary
  499.                 return (b,1)
  500.         else:
  501.             # Conditional bp.
  502.             # Ignore count applies only to those bpt hits where the
  503.             # condition evaluates to true.
  504.             try:
  505.                 val = eval(b.cond, frame.f_globals,
  506.                        frame.f_locals) 
  507.                 if val:
  508.                     if b.ignore > 0:
  509.                         b.ignore = b.ignore -1
  510.                         # continue
  511.                     else:
  512.                         return (b,1)
  513.                 # else:
  514.                 #    continue
  515.             except:
  516.                 # if eval fails, most conservative
  517.                 # thing is to stop on breakpoint
  518.                 # regardless of ignore count. 
  519.                 # Don't delete temporary,
  520.                 # as another hint to user.
  521.                 return (b,0)
  522.     return (None, None)
  523.  
  524. # -------------------- testing --------------------
  525.  
  526. class Tdb(Bdb):
  527.     def user_call(self, frame, args):
  528.         name = frame.f_code.co_name
  529.         if not name: name = '???'
  530.         print '+++ call', name, args
  531.     def user_line(self, frame):
  532.         import linecache, string
  533.         name = frame.f_code.co_name
  534.         if not name: name = '???'
  535.         fn = self.canonic(frame.f_code.co_filename)
  536.         line = linecache.getline(fn, frame.f_lineno)
  537.         print '+++', fn, frame.f_lineno, name, ':', string.strip(line)
  538.     def user_return(self, frame, retval):
  539.         print '+++ return', retval
  540.     def user_exception(self, frame, exc_stuff):
  541.         print '+++ exception', exc_stuff
  542.         self.set_continue()
  543.  
  544. def foo(n):
  545.     print 'foo(', n, ')'
  546.     x = bar(n*10)
  547.     print 'bar returned', x
  548.  
  549. def bar(a):
  550.     print 'bar(', a, ')'
  551.     return a/2
  552.  
  553. def test():
  554.     t = Tdb()
  555.     t.run('import bdb; bdb.foo(10)')
  556.