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

Commit95e0c37

Browse files
committed
Add support for chained exceptions.
Closesipython#13982This is a "backport" ofpython/cpython#106676See documentation there
1 parent46c7ccf commit95e0c37

File tree

4 files changed

+140
-5
lines changed

4 files changed

+140
-5
lines changed

‎IPython/core/debugger.py‎

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
importos
109109

110110
fromIPythonimportget_ipython
111+
fromcontextlibimportcontextmanager
111112
fromIPython.utilsimportPyColorize
112113
fromIPython.utilsimportcoloransi,py3compat
113114
fromIPython.core.excolorsimportexception_colors
@@ -127,6 +128,11 @@
127128
DEBUGGERSKIP="__debuggerskip__"
128129

129130

131+
# this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676
132+
# on lower python versions, we backported the feature.
133+
CHAIN_EXCEPTIONS=sys.version_info< (3,13)
134+
135+
130136
defmake_arrow(pad):
131137
"""generate the leading arrow in front of traceback or debugger"""
132138
ifpad>=2:
@@ -185,6 +191,9 @@ class Pdb(OldPdb):
185191
186192
"""
187193

194+
ifCHAIN_EXCEPTIONS:
195+
MAX_CHAINED_EXCEPTION_DEPTH=999
196+
188197
default_predicates= {
189198
"tbhide":True,
190199
"readonly":False,
@@ -281,6 +290,10 @@ def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwarg
281290
# list of predicates we use to skip frames
282291
self._predicates=self.default_predicates
283292

293+
ifCHAIN_EXCEPTIONS:
294+
self._chained_exceptions=tuple()
295+
self._chained_exception_index=0
296+
284297
#
285298
defset_colors(self,scheme):
286299
"""Shorthand access to the color table scheme selector method."""
@@ -330,9 +343,106 @@ def hidden_frames(self, stack):
330343
ip_hide= [hifi>ip_start[0]elseTruefor (i,h)inenumerate(ip_hide)]
331344
returnip_hide
332345

333-
definteraction(self,frame,traceback):
346+
ifCHAIN_EXCEPTIONS:
347+
348+
def_get_tb_and_exceptions(self,tb_or_exc):
349+
"""
350+
Given a tracecack or an exception, return a tuple of chained exceptions
351+
and current traceback to inspect.
352+
This will deal with selecting the right ``__cause__`` or ``__context__``
353+
as well as handling cycles, and return a flattened list of exceptions we
354+
can jump to with do_exceptions.
355+
"""
356+
_exceptions= []
357+
ifisinstance(tb_or_exc,BaseException):
358+
traceback,current=tb_or_exc.__traceback__,tb_or_exc
359+
360+
whilecurrentisnotNone:
361+
ifcurrentin_exceptions:
362+
break
363+
_exceptions.append(current)
364+
ifcurrent.__cause__isnotNone:
365+
current=current.__cause__
366+
elif (
367+
current.__context__isnotNone
368+
andnotcurrent.__suppress_context__
369+
):
370+
current=current.__context__
371+
372+
iflen(_exceptions)>=self.MAX_CHAINED_EXCEPTION_DEPTH:
373+
self.message(
374+
f"More than{self.MAX_CHAINED_EXCEPTION_DEPTH}"
375+
" chained exceptions found, not all exceptions"
376+
"will be browsable with `exceptions`."
377+
)
378+
break
379+
else:
380+
traceback=tb_or_exc
381+
returntuple(reversed(_exceptions)),traceback
382+
383+
@contextmanager
384+
def_hold_exceptions(self,exceptions):
385+
"""
386+
Context manager to ensure proper cleaning of exceptions references
387+
When given a chained exception instead of a traceback,
388+
pdb may hold references to many objects which may leak memory.
389+
We use this context manager to make sure everything is properly cleaned
390+
"""
391+
try:
392+
self._chained_exceptions=exceptions
393+
self._chained_exception_index=len(exceptions)-1
394+
yield
395+
finally:
396+
# we can't put those in forget as otherwise they would
397+
# be cleared on exception change
398+
self._chained_exceptions=tuple()
399+
self._chained_exception_index=0
400+
401+
defdo_exceptions(self,arg):
402+
"""exceptions [number]
403+
List or change current exception in an exception chain.
404+
Without arguments, list all the current exception in the exception
405+
chain. Exceptions will be numbered, with the current exception indicated
406+
with an arrow.
407+
If given an integer as argument, switch to the exception at that index.
408+
"""
409+
ifnotself._chained_exceptions:
410+
self.message(
411+
"Did not find chained exceptions. To move between"
412+
" exceptions, pdb/post_mortem must be given an exception"
413+
" object rather than a traceback."
414+
)
415+
return
416+
ifnotarg:
417+
forix,excinenumerate(self._chained_exceptions):
418+
prompt=">"ifix==self._chained_exception_indexelse" "
419+
rep=repr(exc)
420+
iflen(rep)>80:
421+
rep=rep[:77]+"..."
422+
self.message(f"{prompt}{ix:>3}{rep}")
423+
else:
424+
try:
425+
number=int(arg)
426+
exceptValueError:
427+
self.error("Argument must be an integer")
428+
return
429+
if0<=number<len(self._chained_exceptions):
430+
self._chained_exception_index=number
431+
self.setup(None,self._chained_exceptions[number].__traceback__)
432+
self.print_stack_entry(self.stack[self.curindex])
433+
else:
434+
self.error("No exception with that number")
435+
436+
definteraction(self,frame,tb_or_exc):
334437
try:
335-
OldPdb.interaction(self,frame,traceback)
438+
ifCHAIN_EXCEPTIONS:
439+
# this context manager is part of interaction in 3.13
440+
_chained_exceptions,tb=self._get_tb_and_exceptions(tb_or_exc)
441+
withself._hold_exceptions(_chained_exceptions):
442+
OldPdb.interaction(self,frame,tb)
443+
else:
444+
OldPdb.interaction(self,frame,traceback)
445+
336446
exceptKeyboardInterrupt:
337447
self.stdout.write("\n"+self.shell.get_exception_only())
338448

‎IPython/core/ultratb.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,13 @@ def debugger(self, force: bool = False):
12461246
ifetbandetb.tb_next:
12471247
etb=etb.tb_next
12481248
self.pdb.botframe=etb.tb_frame
1249-
self.pdb.interaction(None,etb)
1249+
# last_value should be deprecated, but last-exc sometimme not set
1250+
# please check why later and remove the getattr.
1251+
exc=sys.last_valueifsys.version_info< (3,12)elsegetattr(sys,"last_exc",sys.last_value)# type: ignore[attr-defined]
1252+
ifexc:
1253+
self.pdb.interaction(None,exc)
1254+
else:
1255+
self.pdb.interaction(None,etb)
12501256

12511257
ifhasattr(self,'tb'):
12521258
delself.tb

‎IPython/terminal/tests/test_debug_magic.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ def test_debug_magic_passes_through_generators():
6868
child.expect_exact('----> 1 for x in gen:')
6969

7070
child.expect(ipdb_prompt)
71-
child.sendline('u')
72-
child.expect_exact('*** Oldest frame')
71+
child.sendline("u")
72+
child.expect_exact(
73+
"*** all frames above hidden, use `skip_hidden False` to get get into those."
74+
)
7375

7476
child.expect(ipdb_prompt)
7577
child.sendline('exit')

‎docs/source/whatsnew/version8.rst‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
============
22
8.x Series
33
============
4+
5+
.. _version 8.15:
6+
7+
IPython 8.15
8+
------------
9+
10+
Medium release of IPython after a couple of month hiatus, and a bit off-schedule.
11+
12+
The main change is the addition of the ability to move between chained
13+
exceptions when using IPdb, this feature was also contributed to upstream Pdb
14+
and is thus native to CPython in Python 3.13+ Though ipdb should support this
15+
feature in older version of Python. I invite you to look at the `CPython changes
16+
and docs <https://github.com/python/cpython/pull/106676>`_ for more details.
17+
18+
I also want o thanks the `D.E. Shaw group<https://www.deshaw.com/>`_ for
19+
suggesting and funding this feature.
20+
421
.. _version 8.14:
522

623
IPython 8.14

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2026 Movatter.jp