From e59ca2afe3a84c7262add278d36a0f890ca379f2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 30 Jul 2010 17:04:28 +0000 Subject: Add "longlist" and "source" commands, ideas borrowed from pdb++ by Antonio Cuni. --- Doc/library/pdb.rst | 12 +++++++ Lib/pdb.py | 75 ++++++++++++++++++++++++++++++--------- Lib/test/test_pdb.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 9 +++++ 5 files changed, 179 insertions(+), 17 deletions(-) diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index 6da5332..e8a457e 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -368,6 +368,12 @@ by the local file. list 11 lines around at that line. With two arguments, list the given range; if the second argument is less than the first, it is interpreted as a count. +.. pdbcommand:: ll | longlist + + List all source code for the current function or frame. + + .. versionadded:: 3.2 + .. pdbcommand:: a(rgs) Print the argument list of the current function. @@ -385,6 +391,12 @@ by the local file. Print the type of the *expression*. +.. pdbcommand:: source expression + + Try to get source code for the given object and display it. + + .. versionadded:: 3.2 + .. _debugger-aliases: .. pdbcommand:: alias [name [command]] diff --git a/Lib/pdb.py b/Lib/pdb.py index 28e29d3..7b444b1 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -74,6 +74,8 @@ import os import re import pprint import traceback +import inspect +import types class Restart(Exception): @@ -1028,25 +1030,62 @@ class Pdb(bdb.Bdb, cmd.Cmd): filename = self.curframe.f_code.co_filename breaklist = self.get_file_breaks(filename) try: - for lineno in range(first, last+1): - line = linecache.getline(filename, lineno, - self.curframe.f_globals) - if not line: - self.message('[EOF]') - break - else: - s = repr(lineno).rjust(3) - if len(s) < 4: s = s + ' ' - if lineno in breaklist: s = s + 'B' - else: s = s + ' ' - if lineno == self.curframe.f_lineno: - s = s + '->' - self.message(s + '\t' + line.rstrip()) - self.lineno = lineno + # XXX add tb_lineno feature + lines = linecache.getlines(filename, self.curframe.f_globals) + self._print_lines(lines[first-1:last], first, breaklist, + self.curframe.f_lineno, -1) + self.lineno = min(last, len(lines)) + if len(lines) < last: + self.message('[EOF]') except KeyboardInterrupt: pass do_l = do_list + def do_longlist(self, arg): + """longlist | ll + List the whole source code for the current function or frame. + """ + filename = self.curframe.f_code.co_filename + breaklist = self.get_file_breaks(filename) + try: + lines, lineno = inspect.getsourcelines(self.curframe) + except IOError as err: + self.error(err) + return + self._print_lines(lines, lineno, breaklist, self.curframe.f_lineno, -1) + do_ll = do_longlist + + def do_source(self, arg): + """source expression + Try to get source code for the given object and display it. + """ + try: + obj = self._getval(arg) + except: + return + try: + lines, lineno = inspect.getsourcelines(obj) + except (IOError, TypeError) as err: + self.error(err) + return + self._print_lines(lines, lineno, [], -1, -1) + + def _print_lines(self, lines, start, breaks, current, special): + """Print a range of lines.""" + for lineno, line in enumerate(lines, start): + s = str(lineno).rjust(3) + if len(s) < 4: + s += ' ' + if lineno in breaks: + s += 'B' + else: + s += ' ' + if lineno == current: + s += '->' + elif lineno == special: + s += '>>' + self.message(s + '\t' + line.rstrip()) + def do_whatis(self, arg): """whatis arg Print the type of the argument. @@ -1249,10 +1288,12 @@ class Pdb(bdb.Bdb, cmd.Cmd): _help_order = [ 'help', 'where', 'down', 'up', 'break', 'tbreak', 'clear', 'disable', 'enable', 'ignore', 'condition', 'commands', 'step', 'next', 'until', - 'jump', 'return', 'retval', 'run', 'continue', 'list', 'args', 'print', - 'whatis', 'alias', 'unalias', 'quit', + 'jump', 'return', 'retval', 'run', 'continue', 'list', 'longlist', + 'args', 'print', 'pp', 'whatis', 'source', 'alias', 'unalias', + 'debug', 'quit', ] +docs = set() for _command in _help_order: __doc__ += getattr(Pdb, 'do_' + _command).__doc__.strip() + '\n\n' __doc__ += Pdb.help_exec.__doc__ diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index aab6962..b2a441d 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -257,6 +257,105 @@ def test_pdb_breakpoint_commands(): """ +def do_nothing(): + pass + +def do_something(): + print(42) + +def test_list_commands(): + """Test the list and source commands of pdb. + + >>> def test_function_2(foo): + ... import test_pdb + ... test_pdb.do_nothing() + ... 'some...' + ... 'more...' + ... 'code...' + ... 'to...' + ... 'make...' + ... 'a...' + ... 'long...' + ... 'listing...' + ... 'useful...' + ... '...' + ... '...' + ... return foo + + >>> def test_function(): + ... import pdb; pdb.Pdb().set_trace() + ... ret = test_function_2('baz') + + >>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + ... 'list', # list first function + ... 'step', # step into second function + ... 'list', # list second function + ... 'list', # continue listing to EOF + ... 'list 1,3', # list specific lines + ... 'list x', # invalid argument + ... 'next', # step to import + ... 'next', # step over import + ... 'step', # step into do_nothing + ... 'longlist', # list all lines + ... 'source do_something', # list all lines of function + ... 'continue', + ... ]): + ... test_function() + > (3)test_function() + -> ret = test_function_2('baz') + (Pdb) list + 1 def test_function(): + 2 import pdb; pdb.Pdb().set_trace() + 3 -> ret = test_function_2('baz') + [EOF] + (Pdb) step + --Call-- + > (1)test_function_2() + -> def test_function_2(foo): + (Pdb) list + 1 -> def test_function_2(foo): + 2 import test_pdb + 3 test_pdb.do_nothing() + 4 'some...' + 5 'more...' + 6 'code...' + 7 'to...' + 8 'make...' + 9 'a...' + 10 'long...' + 11 'listing...' + (Pdb) list + 12 'useful...' + 13 '...' + 14 '...' + 15 return foo + [EOF] + (Pdb) list 1,3 + 1 -> def test_function_2(foo): + 2 import test_pdb + 3 test_pdb.do_nothing() + (Pdb) list x + *** ... + (Pdb) next + > (2)test_function_2() + -> import test_pdb + (Pdb) next + > (3)test_function_2() + -> test_pdb.do_nothing() + (Pdb) step + --Call-- + > /home/gbr/devel/python/Lib/test/test_pdb.py(260)do_nothing() + -> def do_nothing(): + (Pdb) longlist + ... -> def do_nothing(): + ... pass + (Pdb) source do_something + ... def do_something(): + ... print(42) + (Pdb) continue + """ + + def test_pdb_skip_modules(): """This illustrates the simple case of module skipping. diff --git a/Misc/ACKS b/Misc/ACKS index a94318b..3c186b4 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -180,6 +180,7 @@ Drew Csillag Joaquin Cuenca Abela John Cugini Tom Culliton +Antonio Cuni Brian Curtin Lisandro Dalcin Andrew Dalke diff --git a/Misc/NEWS b/Misc/NEWS index 65346fc..ef7433b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -475,6 +475,15 @@ C-API Library ------- +- The pdb command "source" has been added. It displays the source + code for a given object, if possible. + +- The pdb command "longlist" has been added. It displays the whole + source code for the current function. + +- Issue #1503502: Make pdb.Pdb easier to subclass by putting message + and error output into methods. + - Issue #809887: Make the output of pdb's breakpoint deletions more consistent; emit a message when a breakpoint is enabled or disabled. -- cgit v0.12