Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 04143d3

Browse files
committed
Add support for both context and cause.
It should be rare to have both, but now we can explore the full tree of exception. We do a Breadth first traversal of the exception cause/context to be able to jump to any of those. The UI is not optimal, but I'm unsure if there is a better way to show it. I'm thinking a tree, but that might be overwhelming.
1 parent 3096a5b commit 04143d3

File tree

2 files changed

+113
-14
lines changed

2 files changed

+113
-14
lines changed

Lib/pdb.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def namespace(self):
207207

208208
class Pdb(bdb.Bdb, cmd.Cmd):
209209
_chained_exceptions = []
210-
_chained_exception_index = 0
210+
_chained_exception_index = None
211211

212212
_previous_sigint_handler = None
213213

@@ -422,12 +422,20 @@ def interaction(self, frame, tb_or_exc):
422422
if isinstance(tb_or_exc, BaseException):
423423
tb_or_exc, exception = tb_or_exc.__traceback__, tb_or_exc
424424
self._chained_exceptions = [exception]
425-
current = exception
426-
while current := current.__context__:
427-
self._chained_exception_index += 1
428-
self._chained_exceptions.insert(0, current)
425+
self._chained_exception_index = 0
426+
open_list = [exception]
427+
while open_list:
428+
current = open_list.pop(0)
429+
for ex_type in ["__cause__", "__context__"]:
430+
next_ex = getattr(current, ex_type, None)
431+
if next_ex and next_ex not in self._chained_exceptions:
432+
open_list.append(next_ex)
433+
self._chained_exception_index += 1
434+
self._chained_exceptions.insert(0, next_ex)
435+
429436
else:
430437
self._chained_exceptions = []
438+
self._chained_exception_index = None
431439

432440
if Pdb._previous_sigint_handler:
433441
try:

Lib/test/test_pdb.py

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ def test_convenience_variables():
827827
"""
828828

829829

830-
def test_post_mortem_chained():
830+
def test_post_mortem_simple_chained():
831831
"""Test post mortem traceback debugging of chained exception
832832
833833
>>> def test_function_2():
@@ -870,40 +870,131 @@ def test_post_mortem_chained():
870870
... except ZeroDivisionError:
871871
... print('Correctly reraised.')
872872
Exception!
873-
> <doctest test.test_pdb.test_post_mortem_chained[1]>(5)test_function_reraise()
873+
> <doctest test.test_pdb.test_post_mortem_simple_chained[1]>(5)test_function_reraise()
874874
-> raise ZeroDivisionError('reraised') from e
875875
(Pdb) exceptions
876876
0 ZeroDivisionError('division by zero')
877877
> 1 ZeroDivisionError('reraised')
878878
(Pdb) exceptions 0
879-
> <doctest test.test_pdb.test_post_mortem_chained[0]>(3)test_function_2()
879+
> <doctest test.test_pdb.test_post_mortem_simple_chained[0]>(3)test_function_2()
880880
-> 1/0
881881
(Pdb) up
882-
> <doctest test.test_pdb.test_post_mortem_chained[1]>(3)test_function_reraise()
882+
> <doctest test.test_pdb.test_post_mortem_simple_chained[1]>(3)test_function_reraise()
883883
-> test_function_2()
884884
(Pdb) down
885-
> <doctest test.test_pdb.test_post_mortem_chained[0]>(3)test_function_2()
885+
> <doctest test.test_pdb.test_post_mortem_simple_chained[0]>(3)test_function_2()
886886
-> 1/0
887887
(Pdb) exceptions 1
888-
> <doctest test.test_pdb.test_post_mortem_chained[1]>(5)test_function_reraise()
888+
> <doctest test.test_pdb.test_post_mortem_simple_chained[1]>(5)test_function_reraise()
889889
-> raise ZeroDivisionError('reraised') from e
890890
(Pdb) up
891-
> <doctest test.test_pdb.test_post_mortem_chained[2]>(5)test_function()
891+
> <doctest test.test_pdb.test_post_mortem_simple_chained[2]>(5)test_function()
892892
-> test_function_reraise()
893893
(Pdb) down
894-
> <doctest test.test_pdb.test_post_mortem_chained[1]>(5)test_function_reraise()
894+
> <doctest test.test_pdb.test_post_mortem_simple_chained[1]>(5)test_function_reraise()
895895
-> raise ZeroDivisionError('reraised') from e
896896
(Pdb) exceptions -1
897897
*** No exception with that number
898898
(Pdb) exceptions 3
899899
*** No exception with that number
900900
(Pdb) up
901-
> <doctest test.test_pdb.test_post_mortem_chained[2]>(5)test_function()
901+
> <doctest test.test_pdb.test_post_mortem_simple_chained[2]>(5)test_function()
902902
-> test_function_reraise()
903903
(Pdb) exit
904904
"""
905905

906906

907+
def test_post_mortem_context_and_cause():
908+
"""Test post mortem traceback debugging of chained exception
909+
910+
>>> def cause():
911+
... try:
912+
... raise ValueError("Cause Leaf")
913+
... except Exception as e:
914+
... raise ValueError("With Cause") from e
915+
916+
>>> def context():
917+
... try:
918+
... raise ValueError("Context Leaf")
919+
... except Exception as e:
920+
... raise ValueError("With Context") from e
921+
922+
>>> def main():
923+
... try:
924+
... context()
925+
... except Exception as e1:
926+
... try:
927+
... cause()
928+
... except Exception as e2:
929+
... ex = e2
930+
... raise ValueError("With Context and With Cause") from ex
931+
932+
>>> def test_function():
933+
... import pdb;
934+
... instance = pdb.Pdb(nosigint=True, readrc=False)
935+
... try:
936+
... main()
937+
... except Exception as e:
938+
... # same as pdb.post_mortem(e), but with custom pdb instance.
939+
... instance.reset()
940+
... instance.interaction(None, e)
941+
942+
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
943+
... 'exceptions',
944+
... 'exceptions 2',
945+
... 'up',
946+
... 'down',
947+
... 'exceptions 3',
948+
... 'up',
949+
... 'down',
950+
... 'exceptions 4',
951+
... 'up',
952+
... 'down',
953+
... 'exit',
954+
... ]):
955+
... try:
956+
... test_function()
957+
... except ValueError:
958+
... print('Correctly reraised.')
959+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[2]>(9)main()
960+
-> raise ValueError("With Context and With Cause") from ex
961+
(Pdb) exceptions
962+
0 ValueError('Cause Leaf')
963+
1 ValueError('Context Leaf')
964+
2 ValueError('With Cause')
965+
3 ValueError('With Context')
966+
> 4 ValueError('With Context and With Cause')
967+
(Pdb) exceptions 2
968+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[0]>(5)cause()
969+
-> raise ValueError("With Cause") from e
970+
(Pdb) up
971+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[2]>(6)main()
972+
-> cause()
973+
(Pdb) down
974+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[0]>(5)cause()
975+
-> raise ValueError("With Cause") from e
976+
(Pdb) exceptions 3
977+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[1]>(5)context()
978+
-> raise ValueError("With Context") from e
979+
(Pdb) up
980+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[2]>(3)main()
981+
-> context()
982+
(Pdb) down
983+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[1]>(5)context()
984+
-> raise ValueError("With Context") from e
985+
(Pdb) exceptions 4
986+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[2]>(9)main()
987+
-> raise ValueError("With Context and With Cause") from ex
988+
(Pdb) up
989+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[3]>(5)test_function()
990+
-> main()
991+
(Pdb) down
992+
> <doctest test.test_pdb.test_post_mortem_context_and_cause[2]>(9)main()
993+
-> raise ValueError("With Context and With Cause") from ex
994+
(Pdb) exit
995+
"""
996+
997+
907998
def test_post_mortem():
908999
"""Test post mortem traceback debugging.
9091000

0 commit comments

Comments
 (0)