summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xLib/pdb.py29
-rw-r--r--Lib/test/test_pdb.py109
2 files changed, 108 insertions, 30 deletions
diff --git a/Lib/pdb.py b/Lib/pdb.py
index b603aca..c31f608 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -494,6 +494,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
Pdb._previous_sigint_handler = None
_chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
+ if isinstance(tb_or_exc, BaseException):
+ assert tb is not None, "main exception must have a traceback"
with self._hold_exceptions(_chained_exceptions):
if self.setup(frame, tb):
# no interaction desired at this time (happens if .pdbrc contains
@@ -1169,7 +1171,12 @@ class Pdb(bdb.Bdb, cmd.Cmd):
rep = repr(exc)
if len(rep) > 80:
rep = rep[:77] + "..."
- self.message(f"{prompt} {ix:>3} {rep}")
+ indicator = (
+ " -"
+ if self._chained_exceptions[ix].__traceback__ is None
+ else f"{ix:>3}"
+ )
+ self.message(f"{prompt} {indicator} {rep}")
else:
try:
number = int(arg)
@@ -1177,6 +1184,10 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.error("Argument must be an integer")
return
if 0 <= number < len(self._chained_exceptions):
+ if self._chained_exceptions[number].__traceback__ is None:
+ self.error("This exception does not have a traceback, cannot jump to it")
+ return
+
self._chained_exception_index = number
self.setup(None, self._chained_exceptions[number].__traceback__)
self.print_stack_entry(self.stack[self.curindex])
@@ -2010,19 +2021,27 @@ def post_mortem(t=None):
If `t` is an exception object, the `exceptions` command makes it possible to
list and inspect its chained exceptions (if any).
"""
+ return _post_mortem(t, Pdb())
+
+
+def _post_mortem(t, pdb_instance):
+ """
+ Private version of post_mortem, which allow to pass a pdb instance
+ for testing purposes.
+ """
# handling the default
if t is None:
exc = sys.exception()
if exc is not None:
t = exc.__traceback__
- if t is None:
+ if t is None or (isinstance(t, BaseException) and t.__traceback__ is None):
raise ValueError("A valid traceback must be passed if no "
"exception is being handled")
- p = Pdb()
- p.reset()
- p.interaction(None, t)
+ pdb_instance.reset()
+ pdb_instance.interaction(None, t)
+
def pm():
"""Enter post-mortem debugging of the traceback found in sys.last_exc."""
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index a9edd1a..f6bed84 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -848,9 +848,7 @@ def test_post_mortem_chained():
... try:
... test_function_reraise()
... except Exception as e:
- ... # same as pdb.post_mortem(e), but with custom pdb instance.
- ... instance.reset()
- ... instance.interaction(None, e)
+ ... pdb._post_mortem(e, instance)
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
... 'exceptions',
@@ -907,11 +905,18 @@ def test_post_mortem_chained():
def test_post_mortem_cause_no_context():
"""Test post mortem traceback debugging of chained exception
+ >>> def make_exc_with_stack(type_, *content, from_=None):
+ ... try:
+ ... raise type_(*content) from from_
+ ... except Exception as out:
+ ... return out
+ ...
+
>>> def main():
... try:
... raise ValueError('Context Not Shown')
... except Exception as e1:
- ... raise ValueError("With Cause") from TypeError('The Cause')
+ ... raise ValueError("With Cause") from make_exc_with_stack(TypeError,'The Cause')
>>> def test_function():
... import pdb;
@@ -919,12 +924,11 @@ def test_post_mortem_cause_no_context():
... try:
... main()
... except Exception as e:
- ... # same as pdb.post_mortem(e), but with custom pdb instance.
- ... instance.reset()
- ... instance.interaction(None, e)
+ ... pdb._post_mortem(e, instance)
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
... 'exceptions',
+ ... 'exceptions 0',
... 'exceptions 1',
... 'up',
... 'down',
@@ -934,20 +938,23 @@ def test_post_mortem_cause_no_context():
... test_function()
... except ValueError:
... print('Ok.')
- > <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
- -> raise ValueError("With Cause") from TypeError('The Cause')
+ > <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
+ -> raise ValueError("With Cause") from make_exc_with_stack(TypeError,'The Cause')
(Pdb) exceptions
- 0 TypeError('The Cause')
- > 1 ValueError('With Cause')
+ 0 TypeError('The Cause')
+ > 1 ValueError('With Cause')
+ (Pdb) exceptions 0
+ > <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(3)make_exc_with_stack()
+ -> raise type_(*content) from from_
(Pdb) exceptions 1
- > <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
- -> raise ValueError("With Cause") from TypeError('The Cause')
+ > <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
+ -> raise ValueError("With Cause") from make_exc_with_stack(TypeError,'The Cause')
(Pdb) up
- > <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)test_function()
+ > <doctest test.test_pdb.test_post_mortem_cause_no_context[2]>(5)test_function()
-> main()
(Pdb) down
- > <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
- -> raise ValueError("With Cause") from TypeError('The Cause')
+ > <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
+ -> raise ValueError("With Cause") from make_exc_with_stack(TypeError,'The Cause')
(Pdb) exit"""
@@ -971,9 +978,7 @@ def test_post_mortem_context_of_the_cause():
... try:
... main()
... except Exception as e:
- ... # same as pdb.post_mortem(e), but with custom pdb instance.
- ... instance.reset()
- ... instance.interaction(None, e)
+ ... pdb._post_mortem(e, instance)
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
... 'exceptions',
@@ -1046,9 +1051,7 @@ def test_post_mortem_from_none():
... try:
... main()
... except Exception as e:
- ... # same as pdb.post_mortem(e), but with custom pdb instance.
- ... instance.reset()
- ... instance.interaction(None, e)
+ ... pdb._post_mortem(e, instance)
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
... 'exceptions',
@@ -1066,6 +1069,64 @@ def test_post_mortem_from_none():
"""
+def test_post_mortem_from_no_stack():
+ """Test post mortem traceback debugging of chained exception
+
+ especially when one exception has no stack.
+
+ >>> def main():
+ ... raise Exception() from Exception()
+
+
+ >>> def test_function():
+ ... import pdb;
+ ... instance = pdb.Pdb(nosigint=True, readrc=False)
+ ... try:
+ ... main()
+ ... except Exception as e:
+ ... pdb._post_mortem(e, instance)
+
+ >>> with PdbTestInput( # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+ ... ["exceptions",
+ ... "exceptions 0",
+ ... "exit"],
+ ... ):
+ ... try:
+ ... test_function()
+ ... except ValueError:
+ ... print('Correctly reraised.')
+ > <doctest test.test_pdb.test_post_mortem_from_no_stack[0]>(2)main()
+ -> raise Exception() from Exception()
+ (Pdb) exceptions
+ - Exception()
+ > 1 Exception()
+ (Pdb) exceptions 0
+ *** This exception does not have a traceback, cannot jump to it
+ (Pdb) exit
+ """
+
+
+def test_post_mortem_single_no_stack():
+ """Test post mortem called when origin exception has no stack
+
+
+ >>> def test_function():
+ ... import pdb;
+ ... instance = pdb.Pdb(nosigint=True, readrc=False)
+ ... import sys
+ ... sys.last_exc = Exception()
+ ... pdb._post_mortem(sys.last_exc, instance)
+
+ >>> with PdbTestInput( # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+ ... []
+ ... ):
+ ... try:
+ ... test_function()
+ ... except ValueError as e:
+ ... print(e)
+ A valid traceback must be passed if no exception is being handled
+ """
+
def test_post_mortem_complex():
"""Test post mortem traceback debugging of chained exception
@@ -1130,9 +1191,7 @@ def test_post_mortem_complex():
... try:
... main()
... except Exception as e:
- ... # same as pdb.post_mortem(e), but with custom pdb instance.
- ... instance.reset()
- ... instance.interaction(None, e)
+ ... pdb._post_mortem(e, instance)
>>> with PdbTestInput( # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
... ["exceptions",