Commit 0cda9463 authored by Georg Brandl's avatar Georg Brandl

Add "longlist" and "source" commands, ideas borrowed from pdb++ by Antonio Cuni.

parent 885858e5
...@@ -368,6 +368,12 @@ by the local file. ...@@ -368,6 +368,12 @@ by the local file.
list 11 lines around at that line. With two arguments, list the given range; 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. 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) .. pdbcommand:: a(rgs)
Print the argument list of the current function. Print the argument list of the current function.
...@@ -385,6 +391,12 @@ by the local file. ...@@ -385,6 +391,12 @@ by the local file.
Print the type of the *expression*. 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: .. _debugger-aliases:
.. pdbcommand:: alias [name [command]] .. pdbcommand:: alias [name [command]]
......
...@@ -74,6 +74,8 @@ import os ...@@ -74,6 +74,8 @@ import os
import re import re
import pprint import pprint
import traceback import traceback
import inspect
import types
class Restart(Exception): class Restart(Exception):
...@@ -1028,25 +1030,62 @@ class Pdb(bdb.Bdb, cmd.Cmd): ...@@ -1028,25 +1030,62 @@ class Pdb(bdb.Bdb, cmd.Cmd):
filename = self.curframe.f_code.co_filename filename = self.curframe.f_code.co_filename
breaklist = self.get_file_breaks(filename) breaklist = self.get_file_breaks(filename)
try: try:
for lineno in range(first, last+1): # XXX add tb_lineno feature
line = linecache.getline(filename, lineno, lines = linecache.getlines(filename, self.curframe.f_globals)
self.curframe.f_globals) self._print_lines(lines[first-1:last], first, breaklist,
if not line: self.curframe.f_lineno, -1)
self.lineno = min(last, len(lines))
if len(lines) < last:
self.message('[EOF]') 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
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
do_l = do_list 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): def do_whatis(self, arg):
"""whatis arg """whatis arg
Print the type of the argument. Print the type of the argument.
...@@ -1249,10 +1288,12 @@ class Pdb(bdb.Bdb, cmd.Cmd): ...@@ -1249,10 +1288,12 @@ class Pdb(bdb.Bdb, cmd.Cmd):
_help_order = [ _help_order = [
'help', 'where', 'down', 'up', 'break', 'tbreak', 'clear', 'disable', 'help', 'where', 'down', 'up', 'break', 'tbreak', 'clear', 'disable',
'enable', 'ignore', 'condition', 'commands', 'step', 'next', 'until', 'enable', 'ignore', 'condition', 'commands', 'step', 'next', 'until',
'jump', 'return', 'retval', 'run', 'continue', 'list', 'args', 'print', 'jump', 'return', 'retval', 'run', 'continue', 'list', 'longlist',
'whatis', 'alias', 'unalias', 'quit', 'args', 'print', 'pp', 'whatis', 'source', 'alias', 'unalias',
'debug', 'quit',
] ]
docs = set()
for _command in _help_order: for _command in _help_order:
__doc__ += getattr(Pdb, 'do_' + _command).__doc__.strip() + '\n\n' __doc__ += getattr(Pdb, 'do_' + _command).__doc__.strip() + '\n\n'
__doc__ += Pdb.help_exec.__doc__ __doc__ += Pdb.help_exec.__doc__
......
...@@ -257,6 +257,105 @@ def test_pdb_breakpoint_commands(): ...@@ -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()
> <doctest test.test_pdb.test_list_commands[1]>(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--
> <doctest test.test_pdb.test_list_commands[0]>(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
> <doctest test.test_pdb.test_list_commands[0]>(2)test_function_2()
-> import test_pdb
(Pdb) next
> <doctest test.test_pdb.test_list_commands[0]>(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(): def test_pdb_skip_modules():
"""This illustrates the simple case of module skipping. """This illustrates the simple case of module skipping.
......
...@@ -180,6 +180,7 @@ Drew Csillag ...@@ -180,6 +180,7 @@ Drew Csillag
Joaquin Cuenca Abela Joaquin Cuenca Abela
John Cugini John Cugini
Tom Culliton Tom Culliton
Antonio Cuni
Brian Curtin Brian Curtin
Lisandro Dalcin Lisandro Dalcin
Andrew Dalke Andrew Dalke
......
...@@ -475,6 +475,15 @@ C-API ...@@ -475,6 +475,15 @@ C-API
Library 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 - Issue #809887: Make the output of pdb's breakpoint deletions more
consistent; emit a message when a breakpoint is enabled or disabled. consistent; emit a message when a breakpoint is enabled or disabled.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment