home *** CD-ROM | disk | FTP | other *** search
/ PC Extra 07 & 08 / pca1507.iso / Software / psp8 / Data1.cab / bdb.py < prev    next >
Encoding:
Python Source  |  2003-04-22  |  18.4 KB  |  569 lines

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