Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit79a62de

Browse files
committed
Allow Pdb to move between chained exception.
This lets Pdb receive and exception, instead of a traceback, and whenthis is the case and the exception are chained, `exceptions` allow tolist and move between the chained exceptions when reaching thetot/bottom.That is to say if you have something like def out(): try: middle() # B except Exception as e: raise ValueError("foo(): bar failed") # A def middle(): try: return inner(0) # D except Exception as e: raise ValueError("Middle fail") # C def inner(x): 1 / x # EOnly A is reachable after calling `out()` and doing post mortem debug.With this all A-E points are reachable with a combination of up/down,and ``exception <number>``.I also change the default behavior of ``pdb.pm()``, to receive`sys.last_value` so that chained exception navigation is enabled.Closesgh-106670
1 parent3e65bae commit79a62de

File tree

3 files changed

+165
-1
lines changed

3 files changed

+165
-1
lines changed

‎Lib/pdb.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ def namespace(self):
206206
line_prefix='\n-> '# Probably a better default
207207

208208
classPdb(bdb.Bdb,cmd.Cmd):
209+
_chained_exceptions= []
210+
_chained_index=0
209211

210212
_previous_sigint_handler=None
211213

@@ -416,6 +418,17 @@ def preloop(self):
416418

417419
definteraction(self,frame,traceback):
418420
# Restore the previous signal handler at the Pdb prompt.
421+
422+
ifisinstance(traceback,BaseException):
423+
traceback,exception=traceback.__traceback__,traceback
424+
self._chained_exceptions= [exception]
425+
current=exception
426+
whilecurrent:=current.__context__:
427+
self._chained_index+=1
428+
self._chained_exceptions.insert(0,current)
429+
else:
430+
self._chained_exceptions= []
431+
419432
ifPdb._previous_sigint_handler:
420433
try:
421434
signal.signal(signal.SIGINT,Pdb._previous_sigint_handler)
@@ -1073,6 +1086,26 @@ def _select_frame(self, number):
10731086
self.print_stack_entry(self.stack[self.curindex])
10741087
self.lineno=None
10751088

1089+
defdo_exceptions(self,arg):
1090+
ifnotargorarg=="list":
1091+
forix,excinenumerate(self._chained_exceptions):
1092+
print(">"ifix==self._chained_indexelse" ",ix,repr(exc))
1093+
else:
1094+
try:
1095+
arg=int(arg)
1096+
exceptValueError:
1097+
self.error("Argument must be an integer")
1098+
return
1099+
pass
1100+
else:
1101+
if0<=arg<len(self._chained_exceptions):
1102+
self._chained_index=arg
1103+
self.setup(None,self._chained_exceptions[arg].__traceback__)
1104+
self.curindex=0
1105+
self._select_frame(0)
1106+
return
1107+
self.error("No exception with that number")
1108+
10761109
defdo_up(self,arg):
10771110
"""u(p) [count]
10781111
@@ -1895,6 +1928,10 @@ def post_mortem(t=None):
18951928
If no traceback is given, it uses the one of the exception that is
18961929
currently being handled (an exception must be being handled if the
18971930
default is to be used).
1931+
1932+
If t is an Exception and is a chained exception (i.e it has a __context__),
1933+
pdb will be able to move to the sub-exception when reaching the bottom
1934+
frame.
18981935
"""
18991936
# handling the default
19001937
iftisNone:
@@ -1912,7 +1949,9 @@ def post_mortem(t=None):
19121949

19131950
defpm():
19141951
"""Enter post-mortem debugging of the traceback found in sys.last_traceback."""
1915-
ifhasattr(sys,'last_exc'):
1952+
ifhasattr(sys,"last_value"):
1953+
tb=sys.last_value
1954+
elifhasattr(sys,"last_exc"):
19161955
tb=sys.last_exc.__traceback__
19171956
else:
19181957
tb=sys.last_traceback

‎Lib/test/test_pdb.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,84 @@ def test_convenience_variables():
826826
(Pdb) continue
827827
"""
828828

829+
830+
deftest_post_mortem_chained():
831+
"""Test post mortem traceback debugging of chained exception
832+
833+
>>> def test_function_2():
834+
... try:
835+
... 1/0
836+
... finally:
837+
... print('Exception!')
838+
839+
>>> def test_function_reraise():
840+
... try:
841+
... test_function_2()
842+
... except ZeroDivisionError as e:
843+
... raise ZeroDivisionError('reraised') from e
844+
845+
>>> def test_function():
846+
... import pdb;
847+
... instance = pdb.Pdb(nosigint=True, readrc=False)
848+
... try:
849+
... test_function_reraise()
850+
... except Exception as e:
851+
... # same as pdb.post_mortem(e), but with custom pdb instance.
852+
... instance.reset()
853+
... instance.interaction(None, e)
854+
855+
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
856+
... 'exceptions',
857+
... 'exceptions 0',
858+
... 'down',
859+
... 'up',
860+
... 'exceptions 1',
861+
... 'down',
862+
... 'up',
863+
... 'exceptions -1',
864+
... 'exceptions 3',
865+
... 'down',
866+
... 'exit',
867+
... ]):
868+
... try:
869+
... test_function()
870+
... except ZeroDivisionError:
871+
... print('Correctly reraised.')
872+
Exception!
873+
> <doctest test.test_pdb.test_post_mortem_chained[1]>(5)test_function_reraise()
874+
-> raise ZeroDivisionError('reraised') from e
875+
(Pdb) exceptions
876+
0 ZeroDivisionError('division by zero')
877+
> 1 ZeroDivisionError('reraised')
878+
(Pdb) exceptions 0
879+
> <doctest test.test_pdb.test_post_mortem_chained[1]>(3)test_function_reraise()
880+
-> test_function_2()
881+
(Pdb) down
882+
> <doctest test.test_pdb.test_post_mortem_chained[0]>(3)test_function_2()
883+
-> 1/0
884+
(Pdb) up
885+
> <doctest test.test_pdb.test_post_mortem_chained[1]>(3)test_function_reraise()
886+
-> test_function_2()
887+
(Pdb) exceptions 1
888+
> <doctest test.test_pdb.test_post_mortem_chained[2]>(5)test_function()
889+
-> test_function_reraise()
890+
(Pdb) down
891+
> <doctest test.test_pdb.test_post_mortem_chained[1]>(5)test_function_reraise()
892+
-> raise ZeroDivisionError('reraised') from e
893+
(Pdb) up
894+
> <doctest test.test_pdb.test_post_mortem_chained[2]>(5)test_function()
895+
-> test_function_reraise()
896+
(Pdb) exceptions -1
897+
*** No exception with that number
898+
(Pdb) exceptions 3
899+
*** No exception with that number
900+
(Pdb) down
901+
> <doctest test.test_pdb.test_post_mortem_chained[1]>(5)test_function_reraise()
902+
-> raise ZeroDivisionError('reraised') from e
903+
(Pdb) exit
904+
"""
905+
906+
829907
deftest_post_mortem():
830908
"""Test post mortem traceback debugging.
831909
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
Allow Pdb to move between chained exceptions on post_mortem debugging.
2+
3+
If ``Pdb.post_mortem()`` is called with a chained exception, it will now allow
4+
the user to move between the chained exceptions using ``exceptions`` command to
5+
list exceptions, and ``exception <number>`` to switch to that exception.
6+
7+
We do not differentiate ``__cause__`` and ``__context__``.
8+
9+
That is to say if you have the following code
10+
11+
def out():
12+
try:
13+
middle()
14+
except Exception as e:
15+
raise ValueError("reraise middle() error") from e
16+
17+
def middle():
18+
try:
19+
return inner(0)
20+
except Exception as e:
21+
raise ValueError("Middle fail")
22+
23+
def inner(x):
24+
1 / x
25+
26+
Post mortem debugging allows us to move between the different exception:
27+
28+
>>>import pdb; pdb.pm()
29+
30+
> code.py(5)out()
31+
-> raise ValueError("reraise middle() error") from e
32+
33+
(Pdb) exceptions
34+
0 ZeroDivisionError('division by zero')
35+
1 ValueError('Middle fail')
36+
> 2 ValueError('reraise middle() error')
37+
38+
(Pdb) exceptions 0
39+
> code.py(10)middle()
40+
-> return inner(0)
41+
42+
(Pdb) down
43+
> code.py(16)inner()
44+
-> 1 / x
45+
46+
By default, ``pdb.pm()`` will now try to show the exception based on ``sys.last_value`` to allow moving between
47+
exceptions, instead of ``sys.last_traceback``.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp