Skip to content

Commit fd7f7fe

Browse files
committed
pythongh-106670: Fix Pdb handling of chained Exceptions with no stacks.
The introduction of chained exception in pythongh-106676 would lead to File .../Lib/pdb.py", line 298, in setup self.curframe = self.stack[self.curindex][0] ~~~~~~~~~~^^^^^^^^^^^^^^^ IndexError: list index out of range This fixes that by filtering exceptions that that do not have a stack. Update tests to not use stack-less exceptions when testing another feature, and add an explicit test on how we handle stackless exceptions.
1 parent c884784 commit fd7f7fe

File tree

2 files changed

+85
-11
lines changed

2 files changed

+85
-11
lines changed

Lib/pdb.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ def _get_tb_and_exceptions(self, tb_or_exc):
438438
traceback, current = tb_or_exc.__traceback__, tb_or_exc
439439

440440
while current is not None:
441-
if current in _exceptions:
441+
if current in _exceptions or not current.__traceback__:
442442
break
443443
_exceptions.append(current)
444444
if current.__cause__ is not None:
@@ -491,6 +491,10 @@ def interaction(self, frame, tb_or_exc):
491491
Pdb._previous_sigint_handler = None
492492

493493
_chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
494+
if not _chained_exceptions and isinstance(tb_or_exc, BaseException):
495+
raise ValueError(
496+
"A valid traceback must be passed if no exception is being handled"
497+
)
494498
with self._hold_exceptions(_chained_exceptions):
495499
if self.setup(frame, tb):
496500
# no interaction desired at this time (happens if .pdbrc contains

Lib/test/test_pdb.py

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -907,11 +907,18 @@ def test_post_mortem_chained():
907907
def test_post_mortem_cause_no_context():
908908
"""Test post mortem traceback debugging of chained exception
909909
910+
>>> def make_ex_with_stack(type_, *content, from_=None):
911+
... try:
912+
... raise type_(*content) from from_
913+
... except Exception as out:
914+
... return out
915+
...
916+
910917
>>> def main():
911918
... try:
912919
... raise ValueError('Context Not Shown')
913920
... except Exception as e1:
914-
... raise ValueError("With Cause") from TypeError('The Cause')
921+
... raise ValueError("With Cause") from make_ex_with_stack(TypeError,'The Cause')
915922
916923
>>> def test_function():
917924
... import pdb;
@@ -925,6 +932,7 @@ def test_post_mortem_cause_no_context():
925932
926933
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
927934
... 'exceptions',
935+
... 'exceptions 0',
928936
... 'exceptions 1',
929937
... 'up',
930938
... 'down',
@@ -934,20 +942,23 @@ def test_post_mortem_cause_no_context():
934942
... test_function()
935943
... except ValueError:
936944
... print('Ok.')
937-
> <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
938-
-> raise ValueError("With Cause") from TypeError('The Cause')
945+
> <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
946+
-> raise ValueError("With Cause") from make_ex_with_stack(TypeError,'The Cause')
939947
(Pdb) exceptions
940-
0 TypeError('The Cause')
941-
> 1 ValueError('With Cause')
948+
0 TypeError('The Cause')
949+
> 1 ValueError('With Cause')
950+
(Pdb) exceptions 0
951+
> <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(3)make_ex_with_stack()
952+
-> raise type_(*content) from from_
942953
(Pdb) exceptions 1
943-
> <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
944-
-> raise ValueError("With Cause") from TypeError('The Cause')
954+
> <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
955+
-> raise ValueError("With Cause") from make_ex_with_stack(TypeError,'The Cause')
945956
(Pdb) up
946-
> <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)test_function()
957+
> <doctest test.test_pdb.test_post_mortem_cause_no_context[2]>(5)test_function()
947958
-> main()
948959
(Pdb) down
949-
> <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
950-
-> raise ValueError("With Cause") from TypeError('The Cause')
960+
> <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
961+
-> raise ValueError("With Cause") from make_ex_with_stack(TypeError,'The Cause')
951962
(Pdb) exit"""
952963

953964

@@ -1066,6 +1077,65 @@ def test_post_mortem_from_none():
10661077
"""
10671078

10681079

1080+
def test_post_mortem_from_no_stack():
1081+
"""Test post mortem traceback debugging of chained exception
1082+
1083+
especially when one exception has not stack.
1084+
1085+
>>> def main():
1086+
... raise Exception() from Exception()
1087+
1088+
1089+
>>> def test_function():
1090+
... import pdb;
1091+
... instance = pdb.Pdb(nosigint=True, readrc=False)
1092+
... try:
1093+
... main()
1094+
... except Exception as e:
1095+
... # same as pdb.post_mortem(e), but with custom pdb instance.
1096+
... instance.reset()
1097+
... instance.interaction(None, e)
1098+
1099+
>>> with PdbTestInput( # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
1100+
... ["exceptions",
1101+
... "exit"],
1102+
... ):
1103+
... try:
1104+
... test_function()
1105+
... except ValueError:
1106+
... print('Correctly reraised.')
1107+
> <doctest test.test_pdb.test_post_mortem_from_no_stack[0]>(2)main()
1108+
-> raise Exception() from Exception()
1109+
(Pdb) exceptions
1110+
> 0 Exception()
1111+
(Pdb) exit
1112+
"""
1113+
1114+
1115+
def test_post_mortem_single_no_stack():
1116+
"""Test post mortem called when origin exception has not stack
1117+
1118+
1119+
>>> def test_function():
1120+
... import pdb;
1121+
... instance = pdb.Pdb(nosigint=True, readrc=False)
1122+
... import sys
1123+
... sys.last_exc = Exception()
1124+
... # same as pdb.post_mortem(e), but with custom pdb instance.
1125+
... instance.reset()
1126+
... instance.interaction(None, sys.last_exc)
1127+
1128+
>>> with PdbTestInput( # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
1129+
... []
1130+
... ):
1131+
... try:
1132+
... test_function()
1133+
... except ValueError as e:
1134+
... print(e)
1135+
A valid traceback must be passed if no exception is being handled
1136+
"""
1137+
1138+
10691139
def test_post_mortem_complex():
10701140
"""Test post mortem traceback debugging of chained exception
10711141

0 commit comments

Comments
 (0)