From a02609581689cdd2307b376d533da4ea93f46fb3 Mon Sep 17 00:00:00 2001 From: Kris Jurka Date: Sun, 3 Oct 2021 00:06:47 -0700 Subject: [PATCH] handle an exception without a traceback When an exception does not have a traceback there are no frames to filter, but there's an explicit check for the last frame which will fail if there are no frames. An example exception with this problem can be seen when using concurrent.futures.ProcessPoolExecutor. When it reconstitutes a remote exception it has serialized the traceback to a string it includes as the error message instead of an object that can be inspected. --- CHANGES.rst | 3 +++ src/werkzeug/debug/tbtools.py | 5 +++++ tests/test_debug.py | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a63e0e18f..915ca7dd5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -39,6 +39,9 @@ Unreleased - Fix setting CSP header options on the response. :pr:`2237` - Fix an issue with with the interactive debugger where lines would not expand on click for very long tracebacks. :pr:`2239` +- The interactive debugger handles displaying an exception that does + not have a traceback, such as from ``ProcessPoolExecutor``. + :issue:`2217` Version 2.0.1 diff --git a/src/werkzeug/debug/tbtools.py b/src/werkzeug/debug/tbtools.py index a8e2c96e6..5ffd65422 100644 --- a/src/werkzeug/debug/tbtools.py +++ b/src/werkzeug/debug/tbtools.py @@ -357,6 +357,11 @@ def __init__( tb = tb.tb_next # type: ignore def filter_hidden_frames(self) -> None: + # An exception may not have a traceback to filter frames, such + # as one re-raised from ProcessPoolExecutor. + if not self.frames: + return + new_frames: t.List[Frame] = [] hidden = False diff --git a/tests/test_debug.py b/tests/test_debug.py index 19318c72f..a5c6a7ad0 100644 --- a/tests/test_debug.py +++ b/tests/test_debug.py @@ -6,6 +6,7 @@ from werkzeug.debug import console from werkzeug.debug import DebuggedApplication +from werkzeug.debug import get_current_traceback from werkzeug.debug import get_machine_id from werkzeug.debug.console import HTMLStringO from werkzeug.debug.repr import debug_repr @@ -350,3 +351,12 @@ class MutableException(ValueError): except MutableException: # previously crashed: `TypeError: unhashable type 'MutableException'` Traceback(*sys.exc_info()) + + +def test_exception_without_traceback(): + try: + raise Exception("msg1") + except Exception as e: + # filter_hidden_frames should skip this since it has no traceback + e.__context__ = Exception("msg2") + get_current_traceback()