diff options
author | robot-contrib <robot-contrib@yandex-team.com> | 2023-09-30 10:27:28 +0300 |
---|---|---|
committer | robot-contrib <robot-contrib@yandex-team.com> | 2023-09-30 10:47:10 +0300 |
commit | 5a6373c9d09bbfb7094f9992a4531477bb97829e (patch) | |
tree | ebea8fd55fee858876743312cdf789a1f01487b5 /contrib/python/ipython/py3/IPython/core/debugger.py | |
parent | 15f3c7493474de25a6b23296878bb8f49470d2e6 (diff) | |
download | ydb-5a6373c9d09bbfb7094f9992a4531477bb97829e.tar.gz |
Update contrib/python/ipython/py3 to 8.15.0
Diffstat (limited to 'contrib/python/ipython/py3/IPython/core/debugger.py')
-rw-r--r-- | contrib/python/ipython/py3/IPython/core/debugger.py | 118 |
1 files changed, 114 insertions, 4 deletions
diff --git a/contrib/python/ipython/py3/IPython/core/debugger.py b/contrib/python/ipython/py3/IPython/core/debugger.py index c8082e34e7..30be9fc0d1 100644 --- a/contrib/python/ipython/py3/IPython/core/debugger.py +++ b/contrib/python/ipython/py3/IPython/core/debugger.py @@ -108,6 +108,7 @@ import re import os from IPython import get_ipython +from contextlib import contextmanager from IPython.utils import PyColorize from IPython.utils import coloransi, py3compat from IPython.core.excolors import exception_colors @@ -127,6 +128,11 @@ from pdb import Pdb as OldPdb DEBUGGERSKIP = "__debuggerskip__" +# this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676 +# on lower python versions, we backported the feature. +CHAIN_EXCEPTIONS = sys.version_info < (3, 13) + + def make_arrow(pad): """generate the leading arrow in front of traceback or debugger""" if pad >= 2: @@ -185,6 +191,9 @@ class Pdb(OldPdb): """ + if CHAIN_EXCEPTIONS: + MAX_CHAINED_EXCEPTION_DEPTH = 999 + default_predicates = { "tbhide": True, "readonly": False, @@ -281,6 +290,10 @@ class Pdb(OldPdb): # list of predicates we use to skip frames self._predicates = self.default_predicates + if CHAIN_EXCEPTIONS: + self._chained_exceptions = tuple() + self._chained_exception_index = 0 + # def set_colors(self, scheme): """Shorthand access to the color table scheme selector method.""" @@ -330,9 +343,106 @@ class Pdb(OldPdb): ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)] return ip_hide - def interaction(self, frame, traceback): + if CHAIN_EXCEPTIONS: + + def _get_tb_and_exceptions(self, tb_or_exc): + """ + Given a tracecack or an exception, return a tuple of chained exceptions + and current traceback to inspect. + This will deal with selecting the right ``__cause__`` or ``__context__`` + as well as handling cycles, and return a flattened list of exceptions we + can jump to with do_exceptions. + """ + _exceptions = [] + if isinstance(tb_or_exc, BaseException): + traceback, current = tb_or_exc.__traceback__, tb_or_exc + + while current is not None: + if current in _exceptions: + break + _exceptions.append(current) + if current.__cause__ is not None: + current = current.__cause__ + elif ( + current.__context__ is not None + and not current.__suppress_context__ + ): + current = current.__context__ + + if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH: + self.message( + f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}" + " chained exceptions found, not all exceptions" + "will be browsable with `exceptions`." + ) + break + else: + traceback = tb_or_exc + return tuple(reversed(_exceptions)), traceback + + @contextmanager + def _hold_exceptions(self, exceptions): + """ + Context manager to ensure proper cleaning of exceptions references + When given a chained exception instead of a traceback, + pdb may hold references to many objects which may leak memory. + We use this context manager to make sure everything is properly cleaned + """ + try: + self._chained_exceptions = exceptions + self._chained_exception_index = len(exceptions) - 1 + yield + finally: + # we can't put those in forget as otherwise they would + # be cleared on exception change + self._chained_exceptions = tuple() + self._chained_exception_index = 0 + + def do_exceptions(self, arg): + """exceptions [number] + List or change current exception in an exception chain. + Without arguments, list all the current exception in the exception + chain. Exceptions will be numbered, with the current exception indicated + with an arrow. + If given an integer as argument, switch to the exception at that index. + """ + if not self._chained_exceptions: + self.message( + "Did not find chained exceptions. To move between" + " exceptions, pdb/post_mortem must be given an exception" + " object rather than a traceback." + ) + return + if not arg: + for ix, exc in enumerate(self._chained_exceptions): + prompt = ">" if ix == self._chained_exception_index else " " + rep = repr(exc) + if len(rep) > 80: + rep = rep[:77] + "..." + self.message(f"{prompt} {ix:>3} {rep}") + else: + try: + number = int(arg) + except ValueError: + self.error("Argument must be an integer") + return + if 0 <= number < len(self._chained_exceptions): + self._chained_exception_index = number + self.setup(None, self._chained_exceptions[number].__traceback__) + self.print_stack_entry(self.stack[self.curindex]) + else: + self.error("No exception with that number") + + def interaction(self, frame, tb_or_exc): try: - OldPdb.interaction(self, frame, traceback) + if CHAIN_EXCEPTIONS: + # this context manager is part of interaction in 3.13 + _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc) + with self._hold_exceptions(_chained_exceptions): + OldPdb.interaction(self, frame, tb) + else: + OldPdb.interaction(self, frame, traceback) + except KeyboardInterrupt: self.stdout.write("\n" + self.shell.get_exception_only()) @@ -640,7 +750,7 @@ class Pdb(OldPdb): """ self.lastcmd = 'list' last = None - if arg: + if arg and arg != ".": try: x = eval(arg, {}, {}) if type(x) == type(()): @@ -655,7 +765,7 @@ class Pdb(OldPdb): except: print('*** Error in argument:', repr(arg), file=self.stdout) return - elif self.lineno is None: + elif self.lineno is None or arg == ".": first = max(1, self.curframe.f_lineno - 5) else: first = self.lineno + 1 |