diff -r 2a142141e5fd Lib/pdb.py --- a/Lib/pdb.py Thu Mar 08 20:58:29 2012 -0800 +++ b/Lib/pdb.py Fri Mar 09 11:57:22 2012 +0100 @@ -73,6 +73,7 @@ import cmd import bdb import dis import code +import glob import pprint import signal import inspect @@ -155,6 +156,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): # Try to load readline if it exists try: import readline + # remove some common file name delimiters + readline.set_completer_delims(' \t\n`~@#$%^&*()=+[{]}\\|;:\'",<>?') except ImportError: pass self.allow_kbdint = False @@ -445,6 +448,46 @@ class Pdb(bdb.Bdb, cmd.Cmd): def error(self, msg): print('***', msg, file=self.stdout) + # Generic completion functions. complete_foo methods can be assigned below + # to one of these functions. + + def _complete_location(self, text, line, begidx, endidx): + # Complete a location for break/tbreak/clear. + if line.strip().endswith((':', ',')): + # here comes a line number or a condition which we can't complete + return [] + # first, try to find matching functions (i.e. expressions) + try: + ret = self._complete_expression(text, line, begidx, endidx) + # we don't want the parens for callables that rlcompleter adds + ret = [obj.rstrip('(') for obj in ret] + except Exception: + ret = [] + globs = glob.glob(text + '*') + for fn in globs: + if os.path.isdir(fn): + ret.append(fn + '/') + elif os.path.isfile(fn) and fn.lower().endswith(('.py', '.pyw')): + ret.append(fn + ':') + return ret + + def _complete_bpnumber(self, text, line, begidx, endidx): + # Complete a breakpoint number. + return [str(i) for i, bp in enumerate(bdb.Breakpoint.bpbynumber) + if bp is not None and str(i).startswith(text)] + + def _complete_expression(self, text, line, begidx, endidx): + # Complete an arbitrary expression. + if not self.curframe: + return [] + import rlcompleter + ns = self.curframe.f_globals.copy() + ns.update(self.curframe_locals) + completer = rlcompleter.Completer(ns) + if '.' in text: + return completer.attr_matches(text) + return completer.global_matches(text) + # Command definitions, called by cmdloop() # The argument is the remaining string on the command line # Return true to exit from the command loop @@ -526,6 +569,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): self.commands_defining = False self.prompt = prompt_back + complete_commands = _complete_bpnumber + def do_break(self, arg, temporary = 0): """b(reak) [ ([filename:]lineno | function) [, condition] ] Without argument, list all breaks. @@ -618,6 +663,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): self.message("Breakpoint %d at %s:%d" % (bp.number, bp.file, bp.line)) + complete_break = _complete_location + # To be overridden in derived debuggers def defaultFile(self): """Produce a reasonable default.""" @@ -635,6 +682,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): """ self.do_break(arg, 1) + complete_tbreak = _complete_location + def lineinfo(self, identifier): failed = (None, None, None) # Input is identifier, may be in single quotes @@ -704,6 +753,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): bp.enable() self.message('Enabled %s' % bp) + complete_enable = _complete_bpnumber + def do_disable(self, arg): """disable bpnumber [bpnumber ...] Disables the breakpoints given as a space separated list of @@ -722,6 +773,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): bp.disable() self.message('Disabled %s' % bp) + complete_disable = _complete_bpnumber + def do_condition(self, arg): """condition bpnumber [condition] Set a new condition for the breakpoint, an expression which @@ -745,6 +798,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): else: self.message('New condition set for breakpoint %d.' % bp.number) + complete_condition = _complete_bpnumber + def do_ignore(self, arg): """ignore bpnumber [count] Set the ignore count for the given breakpoint number. If @@ -776,6 +831,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): self.message('Will stop next time breakpoint %d is reached.' % bp.number) + complete_ignore = _complete_bpnumber + def do_clear(self, arg): """cl(ear) filename:lineno\ncl(ear) [bpnumber [bpnumber...]] With a space separated list of breakpoint numbers, clear @@ -824,6 +881,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): self.message('Deleted %s' % bp) do_cl = do_clear # 'c' is already an abbreviation for 'continue' + complete_clear = _complete_location + def do_where(self, arg): """w(here) Print a stack trace, with the most recent frame at the bottom. @@ -1007,6 +1066,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): sys.settrace(self.trace_dispatch) self.lastcmd = p.lastcmd + complete_debug = _complete_expression + def do_quit(self, arg): """q(uit)\nexit Quit from the debugger. The program being executed is aborted. @@ -1093,6 +1154,10 @@ class Pdb(bdb.Bdb, cmd.Cmd): except: pass + complete_print = _complete_expression + complete_p = _complete_expression + complete_pp = _complete_expression + def do_list(self, arg): """l(ist) [first [,last] | .] @@ -1173,6 +1238,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): return self._print_lines(lines, lineno) + complete_source = _complete_expression + def _print_lines(self, lines, start, breaks=(), frame=None): """Print a range of lines.""" if frame: @@ -1227,6 +1294,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): # None of the above... self.message(type(value)) + complete_whatis = _complete_expression + def do_display(self, arg): """display [expression] @@ -1244,6 +1313,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): self.displaying.setdefault(self.curframe, {})[arg] = val self.message('display %s: %r' % (arg, val)) + complete_display = _complete_expression + def do_undisplay(self, arg): """undisplay [expression] @@ -1259,6 +1330,10 @@ class Pdb(bdb.Bdb, cmd.Cmd): else: self.displaying.pop(self.curframe, None) + def complete_undisplay(self, text, line, begidx, endidx): + return [e for e in self.displaying.get(self.curframe, {}) + if e.startswith(text)] + def do_interact(self, arg): """interact @@ -1313,6 +1388,9 @@ class Pdb(bdb.Bdb, cmd.Cmd): if args[0] in self.aliases: del self.aliases[args[0]] + def complete_unalias(self, text, line, begidx, endidx): + return [a for a in self.aliases if a.startswith(text)] + # List of all the commands making the program resume execution. commands_resuming = ['do_continue', 'do_step', 'do_next', 'do_return', 'do_quit', 'do_jump']