home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / bdb.py < prev    next >
Text File  |  2003-12-30  |  18KB  |  565 lines

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