diff --git a/news/508.bugfix.md b/news/508.bugfix.md new file mode 100644 index 00000000..4124aca1 --- /dev/null +++ b/news/508.bugfix.md @@ -0,0 +1 @@ +Fix `__suppress_context__` not being respected in exception rendering diff --git a/src/cleo/ui/exception_trace/inspector.py b/src/cleo/ui/exception_trace/inspector.py index 0cad63e9..5b7bbcbe 100644 --- a/src/cleo/ui/exception_trace/inspector.py +++ b/src/cleo/ui/exception_trace/inspector.py @@ -12,7 +12,9 @@ def __init__(self, exception: BaseException) -> None: self._frames: FrameCollection | None = None self._outer_frames = None self._inner_frames = None - self._previous_exception = exception.__context__ + self._previous_exception = ( + None if exception.__suppress_context__ else exception.__context__ + ) @property def exception(self) -> BaseException: diff --git a/tests/fixtures/exceptions/raiser_with_suppressed_context.py b/tests/fixtures/exceptions/raiser_with_suppressed_context.py new file mode 100644 index 00000000..2691706b --- /dev/null +++ b/tests/fixtures/exceptions/raiser_with_suppressed_context.py @@ -0,0 +1,6 @@ + +def raiser_with_suppressed_context() -> None: + try: + raise ValueError("This error should be suppressed.") + except ValueError: + raise RuntimeError("This error should be displayed.") from None diff --git a/tests/ui/test_exception_trace.py b/tests/ui/test_exception_trace.py index a30327bc..df57a2e7 100644 --- a/tests/ui/test_exception_trace.py +++ b/tests/ui/test_exception_trace.py @@ -11,6 +11,7 @@ from cleo.ui.exception_trace.component import ExceptionTrace from tests.fixtures.exceptions import nested1 from tests.fixtures.exceptions import nested2 +from tests.fixtures.exceptions import raiser_with_suppressed_context from tests.fixtures.exceptions import recursion from tests.fixtures.exceptions import simple @@ -50,7 +51,7 @@ def test_render_debug_better_error_message() -> None: trace.render(io) - lineno = 47 + lineno = 48 expected = f""" Stack trace: @@ -84,7 +85,7 @@ def test_render_debug_better_error_message_recursion_error() -> None: except RecursionError as e: trace = ExceptionTrace(e) - lineno = 83 + lineno = 84 trace.render(io) expected = rf"""^ @@ -131,7 +132,7 @@ def test_render_very_verbose_better_error_message() -> None: expected = f""" Stack trace: - 1 {trace._get_relative_file_path(__file__)}:125 in \ + 1 {trace._get_relative_file_path(__file__)}:126 in \ test_render_very_verbose_better_error_message simple.simple_exception() @@ -183,7 +184,7 @@ def test_render_can_ignore_given_files() -> None: trace.ignore_files_in(rf"^{re.escape(nested1.__file__)}$") trace.render(io) - lineno = 180 + lineno = 181 expected = f""" Stack trace: @@ -221,7 +222,7 @@ def test_render_shows_ignored_files_if_in_debug_mode() -> None: trace.ignore_files_in(rf"^{re.escape(nested1.__file__)}$") trace.render(io) - lineno = 218 + lineno = 219 expected = f""" Stack trace: @@ -344,7 +345,7 @@ def test_simple_render_aborts_if_no_message() -> None: trace = ExceptionTrace(e.value) trace.render(io, simple=True) - lineno = 342 + lineno = 343 expected = f""" AssertionError @@ -364,3 +365,32 @@ def test_simple_render_aborts_if_no_message() -> None: {lineno + 4}│ trace.render(io, simple=True) """ assert expected == io.fetch_output() + + +def test_render_with_suppressed_context() -> None: + io = BufferedIO() + lineno = 6 + + # Arrange: Trigger and catch the exception + with pytest.raises(RuntimeError) as excinfo: + raiser_with_suppressed_context.raiser_with_suppressed_context() + + trace = ExceptionTrace(excinfo.value) + + trace.render(io) + + expected = f"""\ + + RuntimeError + + This error should be displayed. + + at {trace._get_relative_file_path(raiser_with_suppressed_context.__file__)}:{lineno} in raiser_with_suppressed_context + {lineno - 4}│ def raiser_with_suppressed_context() -> None: + {lineno - 3}│ try: + {lineno - 2}│ raise ValueError("This error should be suppressed.") + {lineno - 1}│ except ValueError: + → {lineno + 0}│ raise RuntimeError("This error should be displayed.") from None + {lineno + 1}│ +""" + assert expected == io.fetch_output()