1234 lines · 41.3 KB · Python
RawDownload
#! /usr/bin/env python """A Python debugger.""" # (See pdb.doc for documentation.) import sysimport linecacheimport cmdimport bdbfrom repr import Reprimport osimport reimport pprintimport traceback# Create a custom safe Repr instance and increase its maxstring.# The default of 30 truncates error messages too easily._repr = Repr()_repr.maxstring = 200_saferepr = _repr.repr __all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace",           "post_mortem", "help"] def find_function(funcname, filename):    cre = re.compile(r'def\s+%s\s*[(]' % funcname)    try:        fp = open(filename)    except IOError:        return None    # consumer of this info expects the first line to be 1    lineno = 1    answer = None    while 1:        line = fp.readline()        if line == '':            break        if cre.match(line):            answer = funcname, filename, lineno            break        lineno = lineno + 1    fp.close()    return answer  # Interaction prompt line will separate file and call info from code# text using value of line_prefix string.  A newline and arrow may# be to your liking.  You can set it once pdb is imported using the# command "pdb.line_prefix = '\n% '".# line_prefix = ': '    # Use this to get the old situation backline_prefix = '\n-> '   # Probably a better default class Pdb(bdb.Bdb, cmd.Cmd):     def __init__(self, completekey='tab', stdin=None, stdout=None):        bdb.Bdb.__init__(self)        cmd.Cmd.__init__(self, completekey, stdin, stdout)        if stdout:            self.use_rawinput = 0        self.prompt = '(Pdb) '        self.aliases = {}        self.mainpyfile = ''        self._wait_for_mainpyfile = 0        # Try to load readline if it exists        try:            import readline        except ImportError:            pass         # Read $HOME/.pdbrc and ./.pdbrc        self.rcLines = []        if 'HOME' in os.environ:            envHome = os.environ['HOME']            try:                rcFile = open(os.path.join(envHome, ".pdbrc"))            except IOError:                pass            else:                for line in rcFile.readlines():                    self.rcLines.append(line)                rcFile.close()        try:            rcFile = open(".pdbrc")        except IOError:            pass        else:            for line in rcFile.readlines():                self.rcLines.append(line)            rcFile.close()         self.commands = {} # associates a command list to breakpoint numbers        self.commands_doprompt = {} # for each bp num, tells if the prompt must be disp. after execing the cmd list        self.commands_silent = {} # for each bp num, tells if the stack trace must be disp. after execing the cmd list        self.commands_defining = False # True while in the process of defining a command list        self.commands_bnum = None # The breakpoint number for which we are defining a list     def reset(self):        bdb.Bdb.reset(self)        self.forget()     def forget(self):        self.lineno = None        self.stack = []        self.curindex = 0        self.curframe = None     def setup(self, f, t):        self.forget()        self.stack, self.curindex = self.get_stack(f, t)        self.curframe = self.stack[self.curindex][0]        self.execRcLines()     # Can be executed earlier than 'setup' if desired    def execRcLines(self):        if self.rcLines:            # Make local copy because of recursion            rcLines = self.rcLines            # executed only once            self.rcLines = []            for line in rcLines:                line = line[:-1]                if len(line) > 0 and line[0] != '#':                    self.onecmd(line)     # Override Bdb methods     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."""        if self._wait_for_mainpyfile:            return        if self.stop_here(frame):            print >>self.stdout, '--Call--'            self.interaction(frame, None)     def user_line(self, frame):        """This function is called when we stop or break at this line."""        if self._wait_for_mainpyfile:            if (self.mainpyfile != self.canonic(frame.f_code.co_filename)                or frame.f_lineno<= 0):                return            self._wait_for_mainpyfile = 0        if self.bp_commands(frame):            self.interaction(frame, None)     def bp_commands(self,frame):        """ Call every command that was set for the current active breakpoint (if there is one)        Returns True if the normal interaction function must be called, False otherwise """        #self.currentbp is set in bdb.py in bdb.break_here if a breakpoint was hit        if getattr(self,"currentbp",False) and self.currentbp in self.commands:            currentbp = self.currentbp            self.currentbp = 0            lastcmd_back = self.lastcmd            self.setup(frame, None)            for line in self.commands[currentbp]:                self.onecmd(line)            self.lastcmd = lastcmd_back            if not self.commands_silent[currentbp]:                self.print_stack_entry(self.stack[self.curindex])            if self.commands_doprompt[currentbp]:                self.cmdloop()            self.forget()            return        return 1     def user_return(self, frame, return_value):        """This function is called when a return trap is set here."""        frame.f_locals['__return__'] = return_value        print >>self.stdout, '--Return--'        self.interaction(frame, None)     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):        """This function is called if an exception occurs,        but only if we are to stop at or just below this level."""        frame.f_locals['__exception__'] = exc_type, exc_value        if type(exc_type) == type(''):            exc_type_name = exc_type        else: exc_type_name = exc_type.__name__        print >>self.stdout, exc_type_name + ':', _saferepr(exc_value)        self.interaction(frame, exc_traceback)     # General interaction function     def interaction(self, frame, traceback):        self.setup(frame, traceback)        self.print_stack_entry(self.stack[self.curindex])        self.cmdloop()        self.forget()     def default(self, line):        if line[:1] == '!': line = line[1:]        locals = self.curframe.f_locals        globals = self.curframe.f_globals        try:            code = compile(line + '\n', '<stdin>', 'single')            exec code in globals, locals        except:            t, v = sys.exc_info()[:2]            if type(t) == type(''):                exc_type_name = t            else: exc_type_name = t.__name__            print >>self.stdout, '***', exc_type_name + ':', v     def precmd(self, line):        """Handle alias expansion and ';;' separator."""        if not line.strip():            return line        args = line.split()        while args[0] in self.aliases:            line = self.aliases[args[0]]            ii = 1            for tmpArg in args[1:]:                line = line.replace("%" + str(ii),                                      tmpArg)                ii = ii + 1            line = line.replace("%*", ' '.join(args[1:]))            args = line.split()        # split into ';;' separated commands        # unless it's an alias command        if args[0] != 'alias':            marker = line.find(';;')            if marker >= 0:                # queue up everything after marker                next = line[marker+2:].lstrip()                self.cmdqueue.append(next)                line = line[:marker].rstrip()        return line     def onecmd(self, line):        """Interpret the argument as though it had been typed in response        to the prompt.         Checks whether this line is typed at the normal prompt or in        a breakpoint command list definition.        """        if not self.commands_defining:            return cmd.Cmd.onecmd(self, line)        else:            return self.handle_command_def(line)     def handle_command_def(self,line):        """ Handles one command line during command list definition. """        cmd, arg, line = self.parseline(line)        if cmd == 'silent':            self.commands_silent[self.commands_bnum] = True            return # continue to handle other cmd def in the cmd list        elif cmd == 'end':            self.cmdqueue = []            return 1 # end of cmd list        cmdlist = self.commands[self.commands_bnum]        if (arg):            cmdlist.append(cmd+' '+arg)        else:            cmdlist.append(cmd)        # Determine if we must stop        try:            func = getattr(self, 'do_' + cmd)        except AttributeError:            func = self.default        if func.func_name in self.commands_resuming : # one of the resuming commands.            self.commands_doprompt[self.commands_bnum] = False            self.cmdqueue = []            return 1        return     # Command definitions, called by cmdloop()    # The argument is the remaining string on the command line    # Return true to exit from the command loop     do_h = cmd.Cmd.do_help     def do_commands(self, arg):        """Defines a list of commands associated to a breakpoint        Those commands will be executed whenever the breakpoint causes the program to stop execution."""        if not arg:            bnum = len(bdb.Breakpoint.bpbynumber)-1        else:            try:                bnum = int(arg)            except:                print >>self.stdout, "Usage : commands [bnum]\n        ...\n        end"                return        self.commands_bnum = bnum        self.commands[bnum] = []        self.commands_doprompt[bnum] = True        self.commands_silent[bnum] = False        prompt_back = self.prompt        self.prompt = '(com) '        self.commands_defining = True        self.cmdloop()        self.commands_defining = False        self.prompt = prompt_back     def do_break(self, arg, temporary = 0):        # break [ ([filename:]lineno | function) [, "condition"] ]        if not arg:            if self.breaks:  # There's at least one                print >>self.stdout, "Num Type         Disp Enb   Where"                for bp in bdb.Breakpoint.bpbynumber:                    if bp:                        bp.bpprint(self.stdout)            return        # parse arguments; comma has lowest precedence        # and cannot occur in filename        filename = None        lineno = None        cond = None        comma = arg.find(',')        if comma > 0:            # parse stuff after comma: "condition"            cond = arg[comma+1:].lstrip()            arg = arg[:comma].rstrip()        # parse stuff before comma: [filename:]lineno | function        colon = arg.rfind(':')        funcname = None        if colon >= 0:            filename = arg[:colon].rstrip()            f = self.lookupmodule(filename)            if not f:                print >>self.stdout, '*** ', repr(filename),                print >>self.stdout, 'not found from sys.path'                return            else:                filename = f            arg = arg[colon+1:].lstrip()            try:                lineno = int(arg)            except ValueError, msg:                print >>self.stdout, '*** Bad lineno:', arg                return        else:            # no colon; can be lineno or function            try:                lineno = int(arg)            except ValueError:                try:                    func = eval(arg,                                self.curframe.f_globals,                                self.curframe.f_locals)                except:                    func = arg                try:                    if hasattr(func, 'im_func'):                        func = func.im_func                    code = func.func_code                    #use co_name to identify the bkpt (function names                    #could be aliased, but co_name is invariant)                    funcname = code.co_name                    lineno = code.co_firstlineno                    filename = code.co_filename                except:                    # last thing to try                    (ok, filename, ln) = self.lineinfo(arg)                    if not ok:                        print >>self.stdout, '*** The specified object',                        print >>self.stdout, repr(arg),                        print >>self.stdout, 'is not a function'                        print >>self.stdout, 'or was not found along sys.path.'                        return                    funcname = ok # ok contains a function name                    lineno = int(ln)        if not filename:            filename = self.defaultFile()        # Check for reasonable breakpoint        line = self.checkline(filename, lineno)        if line:            # now set the break point            err = self.set_break(filename, line, temporary, cond, funcname)            if err: print >>self.stdout, '***', err            else:                bp = self.get_breaks(filename, line)[-1]                print >>self.stdout, "Breakpoint %d at %s:%d" % (bp.number,                                                                 bp.file,                                                                 bp.line)     # To be overridden in derived debuggers    def defaultFile(self):        """Produce a reasonable default."""        filename = self.curframe.f_code.co_filename        if filename == '<string>' and self.mainpyfile:            filename = self.mainpyfile        return filename     do_b = do_break     def do_tbreak(self, arg):        self.do_break(arg, 1)     def lineinfo(self, identifier):        failed = (None, None, None)        # Input is identifier, may be in single quotes        idstring = identifier.split("'")        if len(idstring) == 1:            # not in single quotes            id = idstring[0].strip()        elif len(idstring) == 3:            # quoted            id = idstring[1].strip()        else:            return failed        if id == '': return failed        parts = id.split('.')        # Protection for derived debuggers        if parts[0] == 'self':            del parts[0]            if len(parts) == 0:                return failed        # Best first guess at file to look at        fname = self.defaultFile()        if len(parts) == 1:            item = parts[0]        else:            # More than one part.            # First is module, second is method/class            f = self.lookupmodule(parts[0])            if f:                fname = f            item = parts[1]        answer = find_function(item, fname)        return answer or failed     def checkline(self, filename, lineno):        """Check whether specified line seems to be executable.         Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank        line or EOF). Warning: testing is not comprehensive.        """        line = linecache.getline(filename, lineno)        if not line:            print >>self.stdout, 'End of file'            return 0        line = line.strip()        # Don't allow setting breakpoint at a blank line        if (not line or (line[0] == '#') or             (line[:3] == '"""') or line[:3] == "'''"):            print >>self.stdout, '*** Blank or comment'            return 0        return lineno     def do_enable(self, arg):        args = arg.split()        for i in args:            try:                i = int(i)            except ValueError:                print >>self.stdout, 'Breakpoint index %r is not a number' % i                continue             if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):                print >>self.stdout, 'No breakpoint numbered', i                continue             bp = bdb.Breakpoint.bpbynumber[i]            if bp:                bp.enable()     def do_disable(self, arg):        args = arg.split()        for i in args:            try:                i = int(i)            except ValueError:                print >>self.stdout, 'Breakpoint index %r is not a number' % i                continue             if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):                print >>self.stdout, 'No breakpoint numbered', i                continue             bp = bdb.Breakpoint.bpbynumber[i]            if bp:                bp.disable()     def do_condition(self, arg):        # arg is breakpoint number and condition        args = arg.split(' ', 1)        try:            bpnum = int(args[0].strip())        except ValueError:            # something went wrong            print >>self.stdout, \                'Breakpoint index %r is not a number' % args[0]            return        try:            cond = args[1]        except:            cond = None        try:            bp = bdb.Breakpoint.bpbynumber[bpnum]        except IndexError:            print >>self.stdout, 'Breakpoint index %r is not valid' % args[0]            return        if bp:            bp.cond = cond            if not cond:                print >>self.stdout, 'Breakpoint', bpnum,                print >>self.stdout, 'is now unconditional.'     def do_ignore(self,arg):        """arg is bp number followed by ignore count."""

Lines 1–500 of 1234.