bdb.py :  » Mobile » Python-for-PalmOS » Python-1.5.2+reduced-1.0 » Lib » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Mobile » Python for PalmOS 
Python for PalmOS » Python 1.5.2 reduced 1.0 » Lib » bdb.py
"""Debugger basics"""

import sys
import os
import types

BdbQuit = 'bdb.BdbQuit' # Exception to give up completely


class Bdb:
    
    """Generic Python debugger base class.

    This class takes care of details of the trace facility;
    a derived class should implement user interaction.
    The standard debugger class (pdb.Pdb) is an example.
    """

    def __init__(self):
        self.breaks = {}
        self.fncache = {}

    def canonic(self, filename):
        canonic = self.fncache.get(filename)
        if not canonic:
            canonic = os.path.abspath(filename)
            self.fncache[filename] = canonic
        return canonic
    
    def reset(self):
        import linecache
        linecache.checkcache()
        self.botframe = None
        self.stopframe = None
        self.returnframe = None
        self.quitting = 0
    
    def trace_dispatch(self, frame, event, arg):
        if self.quitting:
            return # None
        if event == 'line':
            return self.dispatch_line(frame)
        if event == 'call':
            return self.dispatch_call(frame, arg)
        if event == 'return':
            return self.dispatch_return(frame, arg)
        if event == 'exception':
            return self.dispatch_exception(frame, arg)
        print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
        return self.trace_dispatch
    
    def dispatch_line(self, frame):
        if self.stop_here(frame) or self.break_here(frame):
            self.user_line(frame)
            if self.quitting: raise BdbQuit
        return self.trace_dispatch
    
    def dispatch_call(self, frame, arg):
        # XXX 'arg' is no longer used
        if self.botframe is None:
            # First call of dispatch since reset()
            self.botframe = frame
            return self.trace_dispatch
        if not (self.stop_here(frame) or self.break_anywhere(frame)):
            # No need to trace this function
            return # None
        self.user_call(frame, arg)
        if self.quitting: raise BdbQuit
        return self.trace_dispatch
    
    def dispatch_return(self, frame, arg):
        if self.stop_here(frame) or frame == self.returnframe:
            self.user_return(frame, arg)
            if self.quitting: raise BdbQuit
    
    def dispatch_exception(self, frame, arg):
        if self.stop_here(frame):
            self.user_exception(frame, arg)
            if self.quitting: raise BdbQuit
        return self.trace_dispatch
    
    # Normally derived classes don't override the following
    # methods, but they may if they want to redefine the
    # definition of stopping and breakpoints.
    
    def stop_here(self, frame):
        if self.stopframe is None:
            return 1
        if frame is self.stopframe:
            return 1
        while frame is not None and frame is not self.stopframe:
            if frame is self.botframe:
                return 1
            frame = frame.f_back
        return 0

    def break_here(self, frame):
        filename = self.canonic(frame.f_code.co_filename)
        if not self.breaks.has_key(filename):
            return 0
        lineno = frame.f_lineno
        if not lineno in self.breaks[filename]:
            return 0
        # flag says ok to delete temp. bp
        (bp, flag) = effective(filename, lineno, frame)
        if bp:
            self.currentbp = bp.number
            if (flag and bp.temporary):
                self.do_clear(str(bp.number))
            return 1
        else:
            return 0
    
    def break_anywhere(self, frame):
        return self.breaks.has_key(
            self.canonic(frame.f_code.co_filename))
    
    # Derived classes should override the user_* methods
    # to gain control.
    
    def user_call(self, frame, argument_list):
        """This method is called when there is the remote possibility
        that we ever need to stop in this function."""
        pass
    
    def user_line(self, frame):
        """This method is called when we stop or break at this line."""
        pass
    
    def user_return(self, frame, return_value):
        """This method is called when a return trap is set here."""
        pass
    
    def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
        """This method is called if an exception occurs,
        but only if we are to stop at or just below this level."""
        pass
    
    # Derived classes and clients can call the following methods
    # to affect the stepping state.
    
    def set_step(self):
        """Stop after one line of code."""
        self.stopframe = None
        self.returnframe = None
        self.quitting = 0
    
    def set_next(self, frame):
        """Stop on the next line in or below the given frame."""
        self.stopframe = frame
        self.returnframe = None
        self.quitting = 0
    
    def set_return(self, frame):
        """Stop when returning from the given frame."""
        self.stopframe = frame.f_back
        self.returnframe = frame
        self.quitting = 0
    
    def set_trace(self):
        """Start debugging from here."""
        try:
            1 + ''
        except:
            frame = sys.exc_info()[2].tb_frame.f_back
        self.reset()
        while frame:
            frame.f_trace = self.trace_dispatch
            self.botframe = frame
            frame = frame.f_back
        self.set_step()
        sys.settrace(self.trace_dispatch)

    def set_continue(self):
        # Don't stop except at breakpoints or when finished
        self.stopframe = self.botframe
        self.returnframe = None
        self.quitting = 0
        if not self.breaks:
            # no breakpoints; run without debugger overhead
            sys.settrace(None)
            try:
                1 + ''  # raise an exception
            except:
                frame = sys.exc_info()[2].tb_frame.f_back
            while frame and frame is not self.botframe:
                del frame.f_trace
                frame = frame.f_back
    
    def set_quit(self):
        self.stopframe = self.botframe
        self.returnframe = None
        self.quitting = 1
        sys.settrace(None)
    
    # Derived classes and clients can call the following methods
    # to manipulate breakpoints.  These methods return an
    # error message is something went wrong, None if all is well.
    # Set_break prints out the breakpoint line and file:lineno.
    # Call self.get_*break*() to see the breakpoints or better
    # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
    
    def set_break(self, filename, lineno, temporary=0, cond = None):
        filename = self.canonic(filename)
        import linecache # Import as late as possible
        line = linecache.getline(filename, lineno)
        if not line:
            return 'Line %s:%d does not exist' % (filename,
                                   lineno)
        if not self.breaks.has_key(filename):
            self.breaks[filename] = []
        list = self.breaks[filename]
        if not lineno in list:
            list.append(lineno)
        bp = Breakpoint(filename, lineno, temporary, cond)

    def clear_break(self, filename, lineno):
        filename = self.canonic(filename)
        if not self.breaks.has_key(filename):
            return 'There are no breakpoints in %s' % filename
        if lineno not in self.breaks[filename]:
            return 'There is no breakpoint at %s:%d' % (filename,
                                    lineno)
        # If there's only one bp in the list for that file,line
        # pair, then remove the breaks entry
        for bp in Breakpoint.bplist[filename, lineno][:]:
            bp.deleteMe()
        if not Breakpoint.bplist.has_key((filename, lineno)):
            self.breaks[filename].remove(lineno)
        if not self.breaks[filename]:
            del self.breaks[filename]
    
    def clear_bpbynumber(self, arg):
        try:
            number = int(arg)
        except:
            return 'Non-numeric breakpoint number (%s)' % arg
        try:
            bp = Breakpoint.bpbynumber[number]
        except IndexError:
            return 'Breakpoint number (%d) out of range' % number
        if not bp:
            return 'Breakpoint (%d) already deleted' % number
        self.clear_break(bp.file, bp.line)

    def clear_all_file_breaks(self, filename):
        filename = self.canonic(filename)
        if not self.breaks.has_key(filename):
            return 'There are no breakpoints in %s' % filename
        for line in self.breaks[filename]:
            blist = Breakpoint.bplist[filename, line]
            for bp in blist:
                bp.deleteMe()
        del self.breaks[filename]
    
    def clear_all_breaks(self):
        if not self.breaks:
            return 'There are no breakpoints'
        for bp in Breakpoint.bpbynumber:
            if bp:
                bp.deleteMe()
        self.breaks = {}
    
    def get_break(self, filename, lineno):
        filename = self.canonic(filename)
        return self.breaks.has_key(filename) and \
            lineno in self.breaks[filename]
    
    def get_breaks(self, filename, lineno):
        filename = self.canonic(filename)
        return self.breaks.has_key(filename) and \
            lineno in self.breaks[filename] and \
            Breakpoint.bplist[filename, lineno] or []
    
    def get_file_breaks(self, filename):
        filename = self.canonic(filename)
        if self.breaks.has_key(filename):
            return self.breaks[filename]
        else:
            return []
    
    def get_all_breaks(self):
        return self.breaks
    
    # Derived classes and clients can call the following method
    # to get a data structure representing a stack trace.
    
    def get_stack(self, f, t):
        stack = []
        if t and t.tb_frame is f:
            t = t.tb_next
        while f is not None:
            stack.append((f, f.f_lineno))
            if f is self.botframe:
                break
            f = f.f_back
        stack.reverse()
        i = max(0, len(stack) - 1)
        while t is not None:
            stack.append((t.tb_frame, t.tb_lineno))
            t = t.tb_next
        return stack, i
    
    # 
    
    def format_stack_entry(self, frame_lineno, lprefix=': '):
        import linecache, repr, string
        frame, lineno = frame_lineno
        filename = self.canonic(frame.f_code.co_filename)
        s = filename + '(' + `lineno` + ')'
        if frame.f_code.co_name:
            s = s + frame.f_code.co_name
        else:
            s = s + "<lambda>"
        if frame.f_locals.has_key('__args__'):
            args = frame.f_locals['__args__']
        else:
            args = None
        if args:
            s = s + repr.repr(args)
        else:
            s = s + '()'
        if frame.f_locals.has_key('__return__'):
            rv = frame.f_locals['__return__']
            s = s + '->'
            s = s + repr.repr(rv)
        line = linecache.getline(filename, lineno)
        if line: s = s + lprefix + string.strip(line)
        return s
    
    # The following two methods can be called by clients to use
    # a debugger to debug a statement, given as a string.
    
    def run(self, cmd, globals=None, locals=None):
        if globals is None:
            import __main__
            globals = __main__.__dict__
        if locals is None:
            locals = globals
        self.reset()
        sys.settrace(self.trace_dispatch)
        if not isinstance(cmd, types.CodeType):
            cmd = cmd+'\n'
        try:
            try:
                exec cmd in globals, locals
            except BdbQuit:
                pass
        finally:
            self.quitting = 1
            sys.settrace(None)
    
    def runeval(self, expr, globals=None, locals=None):
        if globals is None:
            import __main__
            globals = __main__.__dict__
        if locals is None:
            locals = globals
        self.reset()
        sys.settrace(self.trace_dispatch)
        if not isinstance(expr, types.CodeType):
            expr = expr+'\n'
        try:
            try:
                return eval(expr, globals, locals)
            except BdbQuit:
                pass
        finally:
            self.quitting = 1
            sys.settrace(None)

    def runctx(self, cmd, globals, locals):
        # B/W compatibility
        self.run(cmd, globals, locals)

    # This method is more useful to debug a single function call.

    def runcall(self, func, *args):
        self.reset()
        sys.settrace(self.trace_dispatch)
        res = None
        try:
            try:
                res = apply(func, args)
            except BdbQuit:
                pass
        finally:
            self.quitting = 1
            sys.settrace(None)
        return res


def set_trace():
    Bdb().set_trace()


class Breakpoint:

    """Breakpoint class

    Implements temporary breakpoints, ignore counts, disabling and
    (re)-enabling, and conditionals.

    Breakpoints are indexed by number through bpbynumber and by
    the file,line tuple using bplist.  The former points to a
    single instance of class Breakpoint.  The latter points to a
    list of such instances since there may be more than one
    breakpoint per line.

    """

    # XXX Keeping state in the class is a mistake -- this means
    # you cannot have more than one active Bdb instance.

    next = 1        # Next bp to be assigned
    bplist = {}     # indexed by (file, lineno) tuple
    bpbynumber = [None] # Each entry is None or an instance of Bpt
                # index 0 is unused, except for marking an
                # effective break .... see effective()

    def __init__(self, file, line, temporary=0, cond = None):
        self.file = file    # This better be in canonical form!
        self.line = line
        self.temporary = temporary
        self.cond = cond
        self.enabled = 1
        self.ignore = 0
        self.hits = 0
        self.number = Breakpoint.next
        Breakpoint.next = Breakpoint.next + 1
        # Build the two lists
        self.bpbynumber.append(self)
        if self.bplist.has_key((file, line)):
            self.bplist[file, line].append(self)
        else:
            self.bplist[file, line] = [self]

        
    def deleteMe(self):
        index = (self.file, self.line)
        self.bpbynumber[self.number] = None   # No longer in list
        self.bplist[index].remove(self)
        if not self.bplist[index]:
            # No more bp for this f:l combo
            del self.bplist[index]

    def enable(self):
        self.enabled = 1

    def disable(self):
        self.enabled = 0

    def bpprint(self):
        if self.temporary:
           disp = 'del  '
        else:
           disp = 'keep '
        if self.enabled:
           disp = disp + 'yes'
        else:
           disp = disp + 'no '
        print '%-4dbreakpoint    %s at %s:%d' % (self.number, disp,
                             self.file, self.line)
        if self.cond:
            print '\tstop only if %s' % (self.cond,)
        if self.ignore:
            print '\tignore next %d hits' % (self.ignore)
        if (self.hits):
            if (self.hits > 1): ss = 's'
            else: ss = ''
            print ('\tbreakpoint already hit %d time%s' %
                   (self.hits, ss))

# -----------end of Breakpoint class----------

# Determines if there is an effective (active) breakpoint at this
# line of code.  Returns breakpoint number or 0 if none
def effective(file, line, frame):
    """Determine which breakpoint for this file:line is to be acted upon.

    Called only if we know there is a bpt at this
    location.  Returns breakpoint that was triggered and a flag
    that indicates if it is ok to delete a temporary bp.

    """
    possibles = Breakpoint.bplist[file,line]
    for i in range(0, len(possibles)):
        b = possibles[i]
        if b.enabled == 0:
            continue
        # Count every hit when bp is enabled
        b.hits = b.hits + 1
        if not b.cond:
            # If unconditional, and ignoring,
            # go on to next, else break
            if b.ignore > 0:
                b.ignore = b.ignore -1
                continue
            else:
                # breakpoint and marker that's ok
                # to delete if temporary
                return (b,1)
        else:
            # Conditional bp.
            # Ignore count applies only to those bpt hits where the
            # condition evaluates to true.
            try:
                val = eval(b.cond, frame.f_globals,
                       frame.f_locals) 
                if val:
                    if b.ignore > 0:
                        b.ignore = b.ignore -1
                        # continue
                    else:
                        return (b,1)
                # else:
                #   continue
            except:
                # if eval fails, most conservative
                # thing is to stop on breakpoint
                # regardless of ignore count. 
                # Don't delete temporary,
                # as another hint to user.
                return (b,0)
    return (None, None)

# -------------------- testing --------------------

class Tdb(Bdb):
    def user_call(self, frame, args):
        name = frame.f_code.co_name
        if not name: name = '???'
        print '+++ call', name, args
    def user_line(self, frame):
        import linecache, string
        name = frame.f_code.co_name
        if not name: name = '???'
        fn = self.canonic(frame.f_code.co_filename)
        line = linecache.getline(fn, frame.f_lineno)
        print '+++', fn, frame.f_lineno, name, ':', string.strip(line)
    def user_return(self, frame, retval):
        print '+++ return', retval
    def user_exception(self, frame, exc_stuff):
        print '+++ exception', exc_stuff
        self.set_continue()

def foo(n):
    print 'foo(', n, ')'
    x = bar(n*10)
    print 'bar returned', x

def bar(a):
    print 'bar(', a, ')'
    return a/2

def test():
    t = Tdb()
    t.run('import bdb; bdb.foo(10)')
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.