diff --git a/changelog/14828.bugfix.rst b/changelog/14828.bugfix.rst new file mode 100644 index 00000000000..c343b8610c1 --- /dev/null +++ b/changelog/14828.bugfix.rst @@ -0,0 +1 @@ +Exception groups raised from another exception now print their cause chain only once in the traceback output. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 3c453b15dd7..b2845885c79 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -1230,7 +1230,9 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR reprcrash = None repr_chain.append((reprtraceback, reprcrash, description)) - if e.__cause__ is not None and self.chain: + if isinstance(e, BaseExceptionGroup) and self.chain: + e = None + elif e.__cause__ is not None and self.chain: e = e.__cause__ excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None description = "The above exception was the direct cause of the following exception:" diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 883a7c5f9b0..b9e6a89761c 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1592,6 +1592,29 @@ def g(): ] ) + def test_exc_chain_repr_exception_group_with_cause(self) -> None: + """An exception group raised from another exception must not print that + exception's cause chain twice.""" + try: + try: + raise RuntimeError("original cause") + except RuntimeError as exc: + raise ExceptionGroup("group", [ValueError("inner")]) from exc + except ExceptionGroup: + excinfo = ExceptionInfo.from_current() + + r = excinfo.getrepr() + file = io.StringIO() + tw = TerminalWriter(file=file) + tw.hasmarkup = False + r.toterminal(tw) + + output = file.getvalue() + assert output.count("RuntimeError: original cause") == 1 + assert output.count("ExceptionGroup: group") == 1 + assert output.count("ValueError: inner") == 1 + assert output.count("The above exception was the direct cause") == 1 + def test_exc_chain_repr_cycle(self, importasmod, tw_mock): __tracebackhide__ = True mod = importasmod(