PageRenderTime 1372ms CodeModel.GetById 111ms app.highlight 961ms RepoModel.GetById 169ms app.codeStats 2ms

/Lib/bdb.py

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