Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/14828.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Exception groups raised from another exception now print their cause chain only once in the traceback output.
4 changes: 3 additions & 1 deletion src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:"
Expand Down
23 changes: 23 additions & 0 deletions testing/code/test_excinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading