PyXR

c:\python24\lib \ bdb.py



0001 """Debugger basics"""
0002 
0003 import sys
0004 import os
0005 import types
0006 
0007 __all__ = ["BdbQuit","Bdb","Breakpoint"]
0008 
0009 class BdbQuit(Exception):
0010     """Exception to give up completely"""
0011 
0012 
0013 class Bdb:
0014 
0015     """Generic Python debugger base class.
0016 
0017     This class takes care of details of the trace facility;
0018     a derived class should implement user interaction.
0019     The standard debugger class (pdb.Pdb) is an example.
0020     """
0021 
0022     def __init__(self):
0023         self.breaks = {}
0024         self.fncache = {}
0025 
0026     def canonic(self, filename):
0027         if filename == "<" + filename[1:-1] + ">":
0028             return filename
0029         canonic = self.fncache.get(filename)
0030         if not canonic:
0031             canonic = os.path.abspath(filename)
0032             canonic = os.path.normcase(canonic)
0033             self.fncache[filename] = canonic
0034         return canonic
0035 
0036     def reset(self):
0037         import linecache
0038         linecache.checkcache()
0039         self.botframe = None
0040         self.stopframe = None
0041         self.returnframe = None
0042         self.quitting = 0
0043 
0044     def trace_dispatch(self, frame, event, arg):
0045         if self.quitting:
0046             return # None
0047         if event == 'line':
0048             return self.dispatch_line(frame)
0049         if event == 'call':
0050             return self.dispatch_call(frame, arg)
0051         if event == 'return':
0052             return self.dispatch_return(frame, arg)
0053         if event == 'exception':
0054             return self.dispatch_exception(frame, arg)
0055         if event == 'c_call':
0056             return self.trace_dispatch
0057         if event == 'c_exception':
0058             return self.trace_dispatch
0059         if event == 'c_return':
0060             return self.trace_dispatch
0061         print 'bdb.Bdb.dispatch: unknown debugging event:', repr(event)
0062         return self.trace_dispatch
0063 
0064     def dispatch_line(self, frame):
0065         if self.stop_here(frame) or self.break_here(frame):
0066             self.user_line(frame)
0067             if self.quitting: raise BdbQuit
0068         return self.trace_dispatch
0069 
0070     def dispatch_call(self, frame, arg):
0071         # XXX 'arg' is no longer used
0072         if self.botframe is None:
0073             # First call of dispatch since reset()
0074             self.botframe = frame.f_back # (CT) Note that this may also be None!
0075             return self.trace_dispatch
0076         if not (self.stop_here(frame) or self.break_anywhere(frame)):
0077             # No need to trace this function
0078             return # None
0079         self.user_call(frame, arg)
0080         if self.quitting: raise BdbQuit
0081         return self.trace_dispatch
0082 
0083     def dispatch_return(self, frame, arg):
0084         if self.stop_here(frame) or frame == self.returnframe:
0085             self.user_return(frame, arg)
0086             if self.quitting: raise BdbQuit
0087         return self.trace_dispatch
0088 
0089     def dispatch_exception(self, frame, arg):
0090         if self.stop_here(frame):
0091             self.user_exception(frame, arg)
0092             if self.quitting: raise BdbQuit
0093         return self.trace_dispatch
0094 
0095     # Normally derived classes don't override the following
0096     # methods, but they may if they want to redefine the
0097     # definition of stopping and breakpoints.
0098 
0099     def stop_here(self, frame):
0100         # (CT) stopframe may now also be None, see dispatch_call.
0101         # (CT) the former test for None is therefore removed from here.
0102         if frame is self.stopframe:
0103             return True
0104         while frame is not None and frame is not self.stopframe:
0105             if frame is self.botframe:
0106                 return True
0107             frame = frame.f_back
0108         return False
0109 
0110     def break_here(self, frame):
0111         filename = self.canonic(frame.f_code.co_filename)
0112         if not filename in self.breaks:
0113             return False
0114         lineno = frame.f_lineno
0115         if not lineno in self.breaks[filename]:
0116             # The line itself has no breakpoint, but maybe the line is the
0117             # first line of a function with breakpoint set by function name.
0118             lineno = frame.f_code.co_firstlineno
0119             if not lineno in self.breaks[filename]:
0120                 return False
0121 
0122         # flag says ok to delete temp. bp
0123         (bp, flag) = effective(filename, lineno, frame)
0124         if bp:
0125             self.currentbp = bp.number
0126             if (flag and bp.temporary):
0127                 self.do_clear(str(bp.number))
0128             return True
0129         else:
0130             return False
0131 
0132     def do_clear(self, arg):
0133         raise NotImplementedError, "subclass of bdb must implement do_clear()"
0134 
0135     def break_anywhere(self, frame):
0136         return self.breaks.has_key(
0137             self.canonic(frame.f_code.co_filename))
0138 
0139     # Derived classes should override the user_* methods
0140     # to gain control.
0141 
0142     def user_call(self, frame, argument_list):
0143         """This method is called when there is the remote possibility
0144         that we ever need to stop in this function."""
0145         pass
0146 
0147     def user_line(self, frame):
0148         """This method is called when we stop or break at this line."""
0149         pass
0150 
0151     def user_return(self, frame, return_value):
0152         """This method is called when a return trap is set here."""
0153         pass
0154 
0155     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
0156         """This method is called if an exception occurs,
0157         but only if we are to stop at or just below this level."""
0158         pass
0159 
0160     # Derived classes and clients can call the following methods
0161     # to affect the stepping state.
0162 
0163     def set_step(self):
0164         """Stop after one line of code."""
0165         self.stopframe = None
0166         self.returnframe = None
0167         self.quitting = 0
0168 
0169     def set_next(self, frame):
0170         """Stop on the next line in or below the given frame."""
0171         self.stopframe = frame
0172         self.returnframe = None
0173         self.quitting = 0
0174 
0175     def set_return(self, frame):
0176         """Stop when returning from the given frame."""
0177         self.stopframe = frame.f_back
0178         self.returnframe = frame
0179         self.quitting = 0
0180 
0181     def set_trace(self):
0182         """Start debugging from here."""
0183         frame = sys._getframe().f_back
0184         self.reset()
0185         while frame:
0186             frame.f_trace = self.trace_dispatch
0187             self.botframe = frame
0188             frame = frame.f_back
0189         self.set_step()
0190         sys.settrace(self.trace_dispatch)
0191 
0192     def set_continue(self):
0193         # Don't stop except at breakpoints or when finished
0194         self.stopframe = self.botframe
0195         self.returnframe = None
0196         self.quitting = 0
0197         if not self.breaks:
0198             # no breakpoints; run without debugger overhead
0199             sys.settrace(None)
0200             frame = sys._getframe().f_back
0201             while frame and frame is not self.botframe:
0202                 del frame.f_trace
0203                 frame = frame.f_back
0204 
0205     def set_quit(self):
0206         self.stopframe = self.botframe
0207         self.returnframe = None
0208         self.quitting = 1
0209         sys.settrace(None)
0210 
0211     # Derived classes and clients can call the following methods
0212     # to manipulate breakpoints.  These methods return an
0213     # error message is something went wrong, None if all is well.
0214     # Set_break prints out the breakpoint line and file:lineno.
0215     # Call self.get_*break*() to see the breakpoints or better
0216     # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
0217 
0218     def set_break(self, filename, lineno, temporary=0, cond = None,
0219                   funcname=None):
0220         filename = self.canonic(filename)
0221         import linecache # Import as late as possible
0222         line = linecache.getline(filename, lineno)
0223         if not line:
0224             return 'Line %s:%d does not exist' % (filename,
0225                                    lineno)
0226         if not filename in self.breaks:
0227             self.breaks[filename] = []
0228         list = self.breaks[filename]
0229         if not lineno in list:
0230             list.append(lineno)
0231         bp = Breakpoint(filename, lineno, temporary, cond, funcname)
0232 
0233     def clear_break(self, filename, lineno):
0234         filename = self.canonic(filename)
0235         if not filename in self.breaks:
0236             return 'There are no breakpoints in %s' % filename
0237         if lineno not in self.breaks[filename]:
0238             return 'There is no breakpoint at %s:%d' % (filename,
0239                                     lineno)
0240         # If there's only one bp in the list for that file,line
0241         # pair, then remove the breaks entry
0242         for bp in Breakpoint.bplist[filename, lineno][:]:
0243             bp.deleteMe()
0244         if not Breakpoint.bplist.has_key((filename, lineno)):
0245             self.breaks[filename].remove(lineno)
0246         if not self.breaks[filename]:
0247             del self.breaks[filename]
0248 
0249     def clear_bpbynumber(self, arg):
0250         try:
0251             number = int(arg)
0252         except:
0253             return 'Non-numeric breakpoint number (%s)' % arg
0254         try:
0255             bp = Breakpoint.bpbynumber[number]
0256         except IndexError:
0257             return 'Breakpoint number (%d) out of range' % number
0258         if not bp:
0259             return 'Breakpoint (%d) already deleted' % number
0260         self.clear_break(bp.file, bp.line)
0261 
0262     def clear_all_file_breaks(self, filename):
0263         filename = self.canonic(filename)
0264         if not filename in self.breaks:
0265             return 'There are no breakpoints in %s' % filename
0266         for line in self.breaks[filename]:
0267             blist = Breakpoint.bplist[filename, line]
0268             for bp in blist:
0269                 bp.deleteMe()
0270         del self.breaks[filename]
0271 
0272     def clear_all_breaks(self):
0273         if not self.breaks:
0274             return 'There are no breakpoints'
0275         for bp in Breakpoint.bpbynumber:
0276             if bp:
0277                 bp.deleteMe()
0278         self.breaks = {}
0279 
0280     def get_break(self, filename, lineno):
0281         filename = self.canonic(filename)
0282         return filename in self.breaks and \
0283             lineno in self.breaks[filename]
0284 
0285     def get_breaks(self, filename, lineno):
0286         filename = self.canonic(filename)
0287         return filename in self.breaks and \
0288             lineno in self.breaks[filename] and \
0289             Breakpoint.bplist[filename, lineno] or []
0290 
0291     def get_file_breaks(self, filename):
0292         filename = self.canonic(filename)
0293         if filename in self.breaks:
0294             return self.breaks[filename]
0295         else:
0296             return []
0297 
0298     def get_all_breaks(self):
0299         return self.breaks
0300 
0301     # Derived classes and clients can call the following method
0302     # to get a data structure representing a stack trace.
0303 
0304     def get_stack(self, f, t):
0305         stack = []
0306         if t and t.tb_frame is f:
0307             t = t.tb_next
0308         while f is not None:
0309             stack.append((f, f.f_lineno))
0310             if f is self.botframe:
0311                 break
0312             f = f.f_back
0313         stack.reverse()
0314         i = max(0, len(stack) - 1)
0315         while t is not None:
0316             stack.append((t.tb_frame, t.tb_lineno))
0317             t = t.tb_next
0318         return stack, i
0319 
0320     #
0321 
0322     def format_stack_entry(self, frame_lineno, lprefix=': '):
0323         import linecache, repr
0324         frame, lineno = frame_lineno
0325         filename = self.canonic(frame.f_code.co_filename)
0326         s = '%s(%r)' % (filename, lineno)
0327         if frame.f_code.co_name:
0328             s = s + frame.f_code.co_name
0329         else:
0330             s = s + "<lambda>"
0331         if '__args__' in frame.f_locals:
0332             args = frame.f_locals['__args__']
0333         else:
0334             args = None
0335         if args:
0336             s = s + repr.repr(args)
0337         else:
0338             s = s + '()'
0339         if '__return__' in frame.f_locals:
0340             rv = frame.f_locals['__return__']
0341             s = s + '->'
0342             s = s + repr.repr(rv)
0343         line = linecache.getline(filename, lineno)
0344         if line: s = s + lprefix + line.strip()
0345         return s
0346 
0347     # The following two methods can be called by clients to use
0348     # a debugger to debug a statement, given as a string.
0349 
0350     def run(self, cmd, globals=None, locals=None):
0351         if globals is None:
0352             import __main__
0353             globals = __main__.__dict__
0354         if locals is None:
0355             locals = globals
0356         self.reset()
0357         sys.settrace(self.trace_dispatch)
0358         if not isinstance(cmd, types.CodeType):
0359             cmd = cmd+'\n'
0360         try:
0361             try:
0362                 exec cmd in globals, locals
0363             except BdbQuit:
0364                 pass
0365         finally:
0366             self.quitting = 1
0367             sys.settrace(None)
0368 
0369     def runeval(self, expr, globals=None, locals=None):
0370         if globals is None:
0371             import __main__
0372             globals = __main__.__dict__
0373         if locals is None:
0374             locals = globals
0375         self.reset()
0376         sys.settrace(self.trace_dispatch)
0377         if not isinstance(expr, types.CodeType):
0378             expr = expr+'\n'
0379         try:
0380             try:
0381                 return eval(expr, globals, locals)
0382             except BdbQuit:
0383                 pass
0384         finally:
0385             self.quitting = 1
0386             sys.settrace(None)
0387 
0388     def runctx(self, cmd, globals, locals):
0389         # B/W compatibility
0390         self.run(cmd, globals, locals)
0391 
0392     # This method is more useful to debug a single function call.
0393 
0394     def runcall(self, func, *args, **kwds):
0395         self.reset()
0396         sys.settrace(self.trace_dispatch)
0397         res = None
0398         try:
0399             try:
0400                 res = func(*args, **kwds)
0401             except BdbQuit:
0402                 pass
0403         finally:
0404             self.quitting = 1
0405             sys.settrace(None)
0406         return res
0407 
0408 
0409 def set_trace():
0410     Bdb().set_trace()
0411 
0412 
0413 class Breakpoint:
0414 
0415     """Breakpoint class
0416 
0417     Implements temporary breakpoints, ignore counts, disabling and
0418     (re)-enabling, and conditionals.
0419 
0420     Breakpoints are indexed by number through bpbynumber and by
0421     the file,line tuple using bplist.  The former points to a
0422     single instance of class Breakpoint.  The latter points to a
0423     list of such instances since there may be more than one
0424     breakpoint per line.
0425 
0426     """
0427 
0428     # XXX Keeping state in the class is a mistake -- this means
0429     # you cannot have more than one active Bdb instance.
0430 
0431     next = 1        # Next bp to be assigned
0432     bplist = {}     # indexed by (file, lineno) tuple
0433     bpbynumber = [None] # Each entry is None or an instance of Bpt
0434                 # index 0 is unused, except for marking an
0435                 # effective break .... see effective()
0436 
0437     def __init__(self, file, line, temporary=0, cond=None, funcname=None):
0438         self.funcname = funcname
0439         # Needed if funcname is not None.
0440         self.func_first_executable_line = None
0441         self.file = file    # This better be in canonical form!
0442         self.line = line
0443         self.temporary = temporary
0444         self.cond = cond
0445         self.enabled = 1
0446         self.ignore = 0
0447         self.hits = 0
0448         self.number = Breakpoint.next
0449         Breakpoint.next = Breakpoint.next + 1
0450         # Build the two lists
0451         self.bpbynumber.append(self)
0452         if self.bplist.has_key((file, line)):
0453             self.bplist[file, line].append(self)
0454         else:
0455             self.bplist[file, line] = [self]
0456 
0457 
0458     def deleteMe(self):
0459         index = (self.file, self.line)
0460         self.bpbynumber[self.number] = None   # No longer in list
0461         self.bplist[index].remove(self)
0462         if not self.bplist[index]:
0463             # No more bp for this f:l combo
0464             del self.bplist[index]
0465 
0466     def enable(self):
0467         self.enabled = 1
0468 
0469     def disable(self):
0470         self.enabled = 0
0471 
0472     def bpprint(self):
0473         if self.temporary:
0474             disp = 'del  '
0475         else:
0476             disp = 'keep '
0477         if self.enabled:
0478             disp = disp + 'yes'
0479         else:
0480             disp = disp + 'no '
0481         print '%-4dbreakpoint    %s at %s:%d' % (self.number, disp,
0482                              self.file, self.line)
0483         if self.cond:
0484             print '\tstop only if %s' % (self.cond,)
0485         if self.ignore:
0486             print '\tignore next %d hits' % (self.ignore)
0487         if (self.hits):
0488             if (self.hits > 1): ss = 's'
0489             else: ss = ''
0490             print ('\tbreakpoint already hit %d time%s' %
0491                    (self.hits, ss))
0492 
0493 # -----------end of Breakpoint class----------
0494 
0495 def checkfuncname(b, frame):
0496     """Check whether we should break here because of `b.funcname`."""
0497     if not b.funcname:
0498         # Breakpoint was set via line number.
0499         if b.line != frame.f_lineno:
0500             # Breakpoint was set at a line with a def statement and the function
0501             # defined is called: don't break.
0502             return False
0503         return True
0504 
0505     # Breakpoint set via function name.
0506 
0507     if frame.f_code.co_name != b.funcname:
0508         # It's not a function call, but rather execution of def statement.
0509         return False
0510 
0511     # We are in the right frame.
0512     if not b.func_first_executable_line:
0513         # The function is entered for the 1st time.
0514         b.func_first_executable_line = frame.f_lineno
0515 
0516     if  b.func_first_executable_line != frame.f_lineno:
0517         # But we are not at the first line number: don't break.
0518         return False
0519     return True
0520 
0521 # Determines if there is an effective (active) breakpoint at this
0522 # line of code.  Returns breakpoint number or 0 if none
0523 def effective(file, line, frame):
0524     """Determine which breakpoint for this file:line is to be acted upon.
0525 
0526     Called only if we know there is a bpt at this
0527     location.  Returns breakpoint that was triggered and a flag
0528     that indicates if it is ok to delete a temporary bp.
0529 
0530     """
0531     possibles = Breakpoint.bplist[file,line]
0532     for i in range(0, len(possibles)):
0533         b = possibles[i]
0534         if b.enabled == 0:
0535             continue
0536         if not checkfuncname(b, frame):
0537             continue
0538         # Count every hit when bp is enabled
0539         b.hits = b.hits + 1
0540         if not b.cond:
0541             # If unconditional, and ignoring,
0542             # go on to next, else break
0543             if b.ignore > 0:
0544                 b.ignore = b.ignore -1
0545                 continue
0546             else:
0547                 # breakpoint and marker that's ok
0548                 # to delete if temporary
0549                 return (b,1)
0550         else:
0551             # Conditional bp.
0552             # Ignore count applies only to those bpt hits where the
0553             # condition evaluates to true.
0554             try:
0555                 val = eval(b.cond, frame.f_globals,
0556                        frame.f_locals)
0557                 if val:
0558                     if b.ignore > 0:
0559                         b.ignore = b.ignore -1
0560                         # continue
0561                     else:
0562                         return (b,1)
0563                 # else:
0564                 #   continue
0565             except:
0566                 # if eval fails, most conservative
0567                 # thing is to stop on breakpoint
0568                 # regardless of ignore count.
0569                 # Don't delete temporary,
0570                 # as another hint to user.
0571                 return (b,0)
0572     return (None, None)
0573 
0574 # -------------------- testing --------------------
0575 
0576 class Tdb(Bdb):
0577     def user_call(self, frame, args):
0578         name = frame.f_code.co_name
0579         if not name: name = '???'
0580         print '+++ call', name, args
0581     def user_line(self, frame):
0582         import linecache
0583         name = frame.f_code.co_name
0584         if not name: name = '???'
0585         fn = self.canonic(frame.f_code.co_filename)
0586         line = linecache.getline(fn, frame.f_lineno)
0587         print '+++', fn, frame.f_lineno, name, ':', line.strip()
0588     def user_return(self, frame, retval):
0589         print '+++ return', retval
0590     def user_exception(self, frame, exc_stuff):
0591         print '+++ exception', exc_stuff
0592         self.set_continue()
0593 
0594 def foo(n):
0595     print 'foo(', n, ')'
0596     x = bar(n*10)
0597     print 'bar returned', x
0598 
0599 def bar(a):
0600     print 'bar(', a, ')'
0601     return a/2
0602 
0603 def test():
0604     t = Tdb()
0605     t.run('import bdb; bdb.foo(10)')
0606 
0607 # end
0608 

Generated by PyXR 0.9.4
SourceForge.net Logo