From fdda27ef90b937daff8bb9d61bde7ee3dcac2d43 Mon Sep 17 00:00:00 2001 From: Umair Anis Date: Mon, 25 Nov 2024 13:21:14 +0530 Subject: [PATCH 1/2] pyflyby-dbg: Support chained exceptions This commit adds support of stepping through chained exceptions with pyflyby debugger. ``` $ cat chained_exc.py ------------------------------------------------------------- def inner(): raise ValueError("inner") def outer(): try: inner() except Exception as e: raise ValueError("outer") from e if __name__ == "__main__": outer() -------------------------------------------------------------------------- $ py /u/anis/scratch/chained_exc.py --------------------------------------------------------------------------- ValueError Traceback (most recent call last) File ~/scratch/chained_exc.py:6, in outer() 5 try: ----> 6 inner() 7 except Exception as e: File ~/scratch/chained_exc.py:2, in inner() 1 def inner(): ----> 2 raise ValueError("inner") ValueError: inner The above exception was the direct cause of the following exception: ValueError Traceback (most recent call last) File ~/scratch/chained_exc.py:11 8 raise ValueError("outer") from e 10 if __name__ == "__main__": ---> 11 outer() File ~/scratch/chained_exc.py:8, in outer() 6 inner() 7 except Exception as e: ----> 8 raise ValueError("outer") from e ValueError: outer > /u/anis/scratch/chained_exc.py(8)outer() 6 inner() 7 except Exception as e: ----> 8 raise ValueError("outer") from e 9 10 if __name__ == "__main__": ipdb> exceptions 0 ValueError('inner') > 1 ValueError('outer') ``` The support uses native Ipython's debugger or python's pdb support for chained exceptions and is available with `exceptions` command under ipdb/pdb command prompt. Implementation notes ============================================================= Starting Py3.13, pdb.interaction() supports chained exceptions in case exception (and not traceback) is specified. This support is backported to IPython8.16 for earlier Python versions. So the conditions where chained exceptions won't be supported from pyflyby's debugger would be with Python version < 3.13 and ipython is not being installed, or IPython's version is lesser than 8.16. Ref: PyInf#14001 Reviewer: dhamodha --- lib/python/pyflyby/_dbg.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/python/pyflyby/_dbg.py b/lib/python/pyflyby/_dbg.py index e1214680..1fe36ee0 100644 --- a/lib/python/pyflyby/_dbg.py +++ b/lib/python/pyflyby/_dbg.py @@ -324,7 +324,27 @@ def _debug_exception(*exc_info, **kwargs): # keep the process waiting for debugger to attach. pdb.postloop = _prompt_continue_waiting_for_debugger print_verbose_tb(*exc_info) - pdb.interaction(None, exc_info[2]) + # Starting Py3.13, pdb.interaction() supports chained exceptions in case + # exception (and not traceback) is specified. This support is backported + # to IPython8.16 for earlier Python versions. So the conditions where + # chained exceptions won't be supported from here would be with the + # Python version < 3.13 and ipython not installed, or IPython's version + # is lesser than 8.16. + tb_or_exc = exc_info[2] + if sys.version_info < (3, 13): + # Check if the instance is of IPython's Pdb and its version. + try: + import IPython + if IPython.__version__ >= '8.16': + from IPython.core.debugger import Pdb as IPdb + # This is expected to be True, hence just a safe check. + if isinstance(pdb, IPdb): + tb_or_exc = exc_info[1] + except ImportError: + pass + else: + tb_or_exc = exc_info[1] + pdb.interaction(None, tb_or_exc) def _debug_code(arg, globals=None, locals=None, auto_import=True, tty="/dev/tty"): From f564ad44a0a43e09007950dc4a74b8463b29b375 Mon Sep 17 00:00:00 2001 From: Umair Anis Date: Wed, 4 Dec 2024 18:53:05 +0530 Subject: [PATCH 2/2] address review comments --- lib/python/pyflyby/_dbg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/python/pyflyby/_dbg.py b/lib/python/pyflyby/_dbg.py index 1fe36ee0..859bf730 100644 --- a/lib/python/pyflyby/_dbg.py +++ b/lib/python/pyflyby/_dbg.py @@ -335,12 +335,12 @@ def _debug_exception(*exc_info, **kwargs): # Check if the instance is of IPython's Pdb and its version. try: import IPython - if IPython.__version__ >= '8.16': + if IPython.version_info >= (8, 16): from IPython.core.debugger import Pdb as IPdb # This is expected to be True, hence just a safe check. if isinstance(pdb, IPdb): tb_or_exc = exc_info[1] - except ImportError: + except ModuleNotFoundError: pass else: tb_or_exc = exc_info[1]