(original) (raw)

changeset: 75521:01a25f638c83 user: Georg Brandl georg@python.org date: Sat Mar 10 22:36:48 2012 +0100 files: Doc/library/pdb.rst Doc/whatsnew/3.3.rst Lib/pdb.py Misc/NEWS description: Close #14210: add command argument completion to pdb: complete file names, global/local variables, aliases diff -r 9afbb3af1693 -r 01a25f638c83 Doc/library/pdb.rst --- a/Doc/library/pdb.rst Sat Mar 10 20:52:16 2012 +0100 +++ b/Doc/library/pdb.rst Sat Mar 10 22:36:48 2012 +0100 @@ -38,6 +38,11 @@ > (1)?() (Pdb) +.. versionchanged:: 3.3 + Tab-completion via the :mod:`readline` module is available for commands and + command arguments, e.g. the current global and local names are offered as + arguments of the ``print`` command. + :file:`pdb.py` can also be invoked as a script to debug other scripts. For example:: diff -r 9afbb3af1693 -r 01a25f638c83 Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst Sat Mar 10 20:52:16 2012 +0100 +++ b/Doc/whatsnew/3.3.rst Sat Mar 10 22:36:48 2012 +0100 @@ -777,6 +777,14 @@ .. TODO add examples and howto to the packaging docs and link to them +pdb +--- + +* Tab-completion is now available not only for command names, but also their + arguments. For example, for the ``break`` command, function and file names + are completed. (Contributed by Georg Brandl in :issue:`14210`) + + pydoc ----- diff -r 9afbb3af1693 -r 01a25f638c83 Lib/pdb.py --- a/Lib/pdb.py Sat Mar 10 20:52:16 2012 +0100 +++ b/Lib/pdb.py Sat Mar 10 22:36:48 2012 +0100 @@ -73,6 +73,7 @@ import bdb import dis import code +import glob import pprint import signal import inspect @@ -155,6 +156,8 @@ # 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,61 @@ def error(self, msg): print('***', msg, file=self.stdout) + # Generic completion functions. Individual complete_foo methods can be + # assigned below to one of these functions. + + def _complete_location(self, text, line, begidx, endidx): + # Complete a file/module/function 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) + except Exception: + ret = [] + # Then, try to complete file names as well. + 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. (This would be more helpful if we could + # display additional info along with the completions, such as file/line + # of the breakpoint.) + 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 [] + # Collect globals and locals. It is usually not really sensible to also + # complete builtins, and they clutter the namespace quite heavily, so we + # leave them out. + ns = self.curframe.f_globals.copy() + ns.update(self.curframe_locals) + if '.' in text: + # Walk an attribute chain up to the last part, similar to what + # rlcompleter does. This will bail if any of the parts are not + # simple attribute access, which is what we want. + dotted = text.split('.') + try: + obj = ns[dotted[0]] + for part in dotted[1:-1]: + obj = getattr(obj, part) + except (KeyError, AttributeError): + return [] + prefix = '.'.join(dotted[:-1]) + '.' + return [prefix + n for n in dir(obj) if n.startswith(dotted[-1])] + else: + # Complete a simple name. + return [n for n in ns.keys() if n.startswith(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 +584,8 @@ 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. @@ -628,6 +688,9 @@ do_b = do_break + complete_break = _complete_location + complete_b = _complete_location + def do_tbreak(self, arg): """tbreak [ ([filename:]lineno | function) [, condition] ] Same arguments as break, but sets a temporary breakpoint: it @@ -635,6 +698,8 @@ """ 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 +769,8 @@ 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 +789,8 @@ 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 +814,8 @@ 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 +847,8 @@ 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 +897,9 @@ self.message('Deleted %s' % bp) do_cl = do_clear # 'c' is already an abbreviation for 'continue' + complete_clear = _complete_location + complete_cl = _complete_location + def do_where(self, arg): """w(here) Print a stack trace, with the most recent frame at the bottom. @@ -1007,6 +1083,8 @@ 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 +1171,10 @@ 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 +1255,8 @@ 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 +1311,8 @@ # None of the above... self.message(type(value)) + complete_whatis = _complete_expression + def do_display(self, arg): """display [expression] @@ -1244,6 +1330,8 @@ 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 +1347,10 @@ 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 +1405,9 @@ 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'] diff -r 9afbb3af1693 -r 01a25f638c83 Misc/NEWS --- a/Misc/NEWS Sat Mar 10 20:52:16 2012 +0100 +++ b/Misc/NEWS Sat Mar 10 22:36:48 2012 +0100 @@ -37,6 +37,9 @@ data or close method) for the Python implementation as well. Drop the no-op TreeBuilder().xml() method from the C implementation. +- Issue #14210: pdb now has tab-completion not only for command names, but + also for their arguments, wherever possible. + Extension Modules -----------------/georg@python.org