Show the traceback line numbers as well as the current line numbers if an exception is being debugged. Courtesy of pdb++ by Antonio Cuni. Also document -> and >> markers for "list".
This commit is contained in:
parent
cdf66a9a7c
commit
0a9c3e91dc
|
@ -368,9 +368,18 @@ 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.
|
||||
|
||||
The current line in the current frame is indicated by ``->``. If an
|
||||
exception is being debugged, the line where the exception was originally
|
||||
raised or propagated is indicated by ``>>``, if it differs from the current
|
||||
line.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
The ``>>`` marker.
|
||||
|
||||
.. pdbcommand:: ll | longlist
|
||||
|
||||
List all source code for the current function or frame.
|
||||
List all source code for the current function or frame. Interesting lines
|
||||
are marked as for :pdbcmd:`list`.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
|
68
Lib/pdb.py
68
Lib/pdb.py
|
@ -70,8 +70,10 @@ import sys
|
|||
import linecache
|
||||
import cmd
|
||||
import bdb
|
||||
import dis
|
||||
import os
|
||||
import re
|
||||
import code
|
||||
import pprint
|
||||
import traceback
|
||||
import inspect
|
||||
|
@ -107,14 +109,22 @@ def find_function(funcname, filename):
|
|||
|
||||
def getsourcelines(obj):
|
||||
lines, lineno = inspect.findsource(obj)
|
||||
if inspect.isframe(obj) and lineno == 0 and \
|
||||
obj.f_globals is obj.f_locals:
|
||||
if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
|
||||
# must be a module frame: do not try to cut a block out of it
|
||||
return lines, 0
|
||||
return lines, 1
|
||||
elif inspect.ismodule(obj):
|
||||
return lines, 0
|
||||
return lines, 1
|
||||
return inspect.getblock(lines[lineno:]), lineno+1
|
||||
|
||||
def lasti2lineno(code, lasti):
|
||||
linestarts = list(dis.findlinestarts(code))
|
||||
linestarts.reverse()
|
||||
for i, lineno in linestarts:
|
||||
if lasti >= i:
|
||||
return lineno
|
||||
return 0
|
||||
|
||||
|
||||
# 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
|
||||
|
@ -133,6 +143,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
self.aliases = {}
|
||||
self.mainpyfile = ''
|
||||
self._wait_for_mainpyfile = 0
|
||||
self.tb_lineno = {}
|
||||
# Try to load readline if it exists
|
||||
try:
|
||||
import readline
|
||||
|
@ -179,10 +190,18 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
self.stack = []
|
||||
self.curindex = 0
|
||||
self.curframe = None
|
||||
self.tb_lineno.clear()
|
||||
|
||||
def setup(self, f, t):
|
||||
def setup(self, f, tb):
|
||||
self.forget()
|
||||
self.stack, self.curindex = self.get_stack(f, t)
|
||||
self.stack, self.curindex = self.get_stack(f, tb)
|
||||
while tb:
|
||||
# when setting up post-mortem debugging with a traceback, save all
|
||||
# the original line numbers to be displayed along the current line
|
||||
# numbers (which can be different, e.g. due to finally clauses)
|
||||
lineno = lasti2lineno(tb.tb_frame.f_code, tb.tb_lasti)
|
||||
self.tb_lineno[tb.tb_frame] = lineno
|
||||
tb = tb.tb_next
|
||||
self.curframe = self.stack[self.curindex][0]
|
||||
# The f_locals dictionary is updated from the actual frame
|
||||
# locals whenever the .f_locals accessor is called, so we
|
||||
|
@ -1005,13 +1024,18 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
|
||||
def do_list(self, arg):
|
||||
"""l(ist) [first [,last] | .]
|
||||
List source code for the current file.
|
||||
Without arguments, list 11 lines around the current line
|
||||
or continue the previous listing.
|
||||
With . as argument, list 11 lines around the current line.
|
||||
With one argument, list 11 lines starting at that line.
|
||||
With two arguments, list the given range;
|
||||
if the second argument is less than the first, it is a count.
|
||||
|
||||
List source code for the current file. Without arguments,
|
||||
list 11 lines around the current line or continue the previous
|
||||
listing. With . as argument, list 11 lines around the current
|
||||
line. With one argument, list 11 lines starting at that line.
|
||||
With two arguments, list the given range; if the second
|
||||
argument is less than the first, it is a count.
|
||||
|
||||
The current line in the current frame is indicated by "->".
|
||||
If an exception is being debugged, the line where the
|
||||
exception was originally raised or propagated is indicated by
|
||||
">>", if it differs from the current line.
|
||||
"""
|
||||
self.lastcmd = 'list'
|
||||
last = None
|
||||
|
@ -1039,10 +1063,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
filename = self.curframe.f_code.co_filename
|
||||
breaklist = self.get_file_breaks(filename)
|
||||
try:
|
||||
# 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.curframe)
|
||||
self.lineno = min(last, len(lines))
|
||||
if len(lines) < last:
|
||||
self.message('[EOF]')
|
||||
|
@ -1061,7 +1084,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
except IOError as err:
|
||||
self.error(err)
|
||||
return
|
||||
self._print_lines(lines, lineno, breaklist, self.curframe.f_lineno, -1)
|
||||
self._print_lines(lines, lineno, breaklist, self.curframe)
|
||||
do_ll = do_longlist
|
||||
|
||||
def do_source(self, arg):
|
||||
|
@ -1077,10 +1100,15 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
except (IOError, TypeError) as err:
|
||||
self.error(err)
|
||||
return
|
||||
self._print_lines(lines, lineno, [], -1, -1)
|
||||
self._print_lines(lines, lineno)
|
||||
|
||||
def _print_lines(self, lines, start, breaks, current, special):
|
||||
def _print_lines(self, lines, start, breaks=(), frame=None):
|
||||
"""Print a range of lines."""
|
||||
if frame:
|
||||
current_lineno = frame.f_lineno
|
||||
exc_lineno = self.tb_lineno.get(frame, -1)
|
||||
else:
|
||||
current_lineno = exc_lineno = -1
|
||||
for lineno, line in enumerate(lines, start):
|
||||
s = str(lineno).rjust(3)
|
||||
if len(s) < 4:
|
||||
|
@ -1089,9 +1117,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||
s += 'B'
|
||||
else:
|
||||
s += ' '
|
||||
if lineno == current:
|
||||
if lineno == current_lineno:
|
||||
s += '->'
|
||||
elif lineno == special:
|
||||
elif lineno == exc_lineno:
|
||||
s += '>>'
|
||||
self.message(s + '\t' + line.rstrip())
|
||||
|
||||
|
|
|
@ -359,6 +359,68 @@ def test_list_commands():
|
|||
"""
|
||||
|
||||
|
||||
def test_post_mortem():
|
||||
"""Test post mortem traceback debugging.
|
||||
|
||||
>>> def test_function_2():
|
||||
... try:
|
||||
... 1/0
|
||||
... finally:
|
||||
... print('Exception!')
|
||||
|
||||
>>> def test_function():
|
||||
... import pdb; pdb.Pdb().set_trace()
|
||||
... test_function_2()
|
||||
... print('Not reached.')
|
||||
|
||||
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
|
||||
... 'next', # step over exception-raising call
|
||||
... 'bt', # get a backtrace
|
||||
... 'list', # list code of test_function()
|
||||
... 'down', # step into test_function_2()
|
||||
... 'list', # list code of test_function_2()
|
||||
... 'continue',
|
||||
... ]):
|
||||
... try:
|
||||
... test_function()
|
||||
... except ZeroDivisionError:
|
||||
... print('Correctly reraised.')
|
||||
> <doctest test.test_pdb.test_post_mortem[1]>(3)test_function()
|
||||
-> test_function_2()
|
||||
(Pdb) next
|
||||
Exception!
|
||||
ZeroDivisionError: division by zero
|
||||
> <doctest test.test_pdb.test_post_mortem[1]>(3)test_function()
|
||||
-> test_function_2()
|
||||
(Pdb) bt
|
||||
...
|
||||
<doctest test.test_pdb.test_post_mortem[2]>(10)<module>()
|
||||
-> test_function()
|
||||
> <doctest test.test_pdb.test_post_mortem[1]>(3)test_function()
|
||||
-> test_function_2()
|
||||
<doctest test.test_pdb.test_post_mortem[0]>(3)test_function_2()
|
||||
-> 1/0
|
||||
(Pdb) list
|
||||
1 def test_function():
|
||||
2 import pdb; pdb.Pdb().set_trace()
|
||||
3 -> test_function_2()
|
||||
4 print('Not reached.')
|
||||
[EOF]
|
||||
(Pdb) down
|
||||
> <doctest test.test_pdb.test_post_mortem[0]>(3)test_function_2()
|
||||
-> 1/0
|
||||
(Pdb) list
|
||||
1 def test_function_2():
|
||||
2 try:
|
||||
3 >> 1/0
|
||||
4 finally:
|
||||
5 -> print('Exception!')
|
||||
[EOF]
|
||||
(Pdb) continue
|
||||
Correctly reraised.
|
||||
"""
|
||||
|
||||
|
||||
def test_pdb_skip_modules():
|
||||
"""This illustrates the simple case of module skipping.
|
||||
|
||||
|
|
|
@ -475,6 +475,10 @@ C-API
|
|||
Library
|
||||
-------
|
||||
|
||||
- For traceback debugging, the pdb listing now also shows the locations
|
||||
where the exception was originally (re)raised, if it differs from the
|
||||
last line executed (e.g. in case of finally clauses).
|
||||
|
||||
- The pdb command "source" has been added. It displays the source
|
||||
code for a given object, if possible.
|
||||
|
||||
|
|
Loading…
Reference in New Issue