Skip to content

Commit 47996bd

Browse files
committed
display single contained exception in excgroups in short test summary info
1 parent 8889e9b commit 47996bd

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

changelog/12943.improvement.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
If a test fails with an exceptiongroup with a single exception, the contained exception will now be displayed in the short test summary info.

src/_pytest/_code/code.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,24 @@ def _truncate_recursive_traceback(
10331033

10341034
def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainRepr:
10351035
repr_chain: list[tuple[ReprTraceback, ReprFileLocation | None, str | None]] = []
1036+
1037+
def _get_single_subexc(
1038+
eg: BaseExceptionGroup[BaseException],
1039+
) -> BaseException | None:
1040+
res: BaseException | None = None
1041+
for subexc in eg.exceptions:
1042+
if res is not None:
1043+
return None
1044+
1045+
if isinstance(subexc, BaseExceptionGroup):
1046+
res = _get_single_subexc(subexc)
1047+
if res is None:
1048+
# there were multiple exceptions in the subgroup
1049+
return None
1050+
else:
1051+
res = subexc
1052+
return res
1053+
10361054
e: BaseException | None = excinfo.value
10371055
excinfo_: ExceptionInfo[BaseException] | None = excinfo
10381056
descr = None
@@ -1041,6 +1059,7 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR
10411059
seen.add(id(e))
10421060

10431061
if excinfo_:
1062+
reprcrash = excinfo_._getreprcrash()
10441063
# Fall back to native traceback as a temporary workaround until
10451064
# full support for exception groups added to ExceptionInfo.
10461065
# See https://github.com/pytest-dev/pytest/issues/9159
@@ -1054,9 +1073,13 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR
10541073
)
10551074
)
10561075
)
1076+
if (
1077+
reprcrash is not None
1078+
and (subexc := _get_single_subexc(e)) is not None
1079+
):
1080+
reprcrash.message = f"[in {type(e).__name__}] {subexc!r}"
10571081
else:
10581082
reprtraceback = self.repr_traceback(excinfo_)
1059-
reprcrash = excinfo_._getreprcrash()
10601083
else:
10611084
# Fallback to native repr if the exception doesn't have a traceback:
10621085
# ExceptionInfo objects require a full traceback to work.

testing/code/test_excinfo.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,63 @@ def test_exceptiongroup(pytester: Pytester, outer_chain, inner_chain) -> None:
17031703
_exceptiongroup_common(pytester, outer_chain, inner_chain, native=False)
17041704

17051705

1706+
def test_exceptiongroup_short_summary_info(pytester: Pytester):
1707+
pytester.makepyfile(
1708+
"""
1709+
import sys
1710+
1711+
if sys.version_info < (3, 11):
1712+
from exceptiongroup import BaseExceptionGroup, ExceptionGroup
1713+
1714+
def test_base() -> None:
1715+
raise BaseExceptionGroup("NOT IN SUMMARY", [SystemExit("a" * 10)])
1716+
1717+
def test_nonbase() -> None:
1718+
raise ExceptionGroup("NOT IN SUMMARY", [ValueError("a" * 10)])
1719+
1720+
def test_nested() -> None:
1721+
raise ExceptionGroup(
1722+
"NOT DISPLAYED", [
1723+
ExceptionGroup("NOT IN SUMMARY", [ValueError("a" * 10)])
1724+
]
1725+
)
1726+
1727+
def test_multiple() -> None:
1728+
raise ExceptionGroup(
1729+
"b" * 10,
1730+
[
1731+
ValueError("NOT IN SUMMARY"),
1732+
TypeError("NOT IN SUMMARY"),
1733+
]
1734+
)
1735+
"""
1736+
)
1737+
result = pytester.runpytest("-vv")
1738+
assert result.ret == 1
1739+
result.stdout.fnmatch_lines(
1740+
[
1741+
"*= short test summary info =*",
1742+
(
1743+
"FAILED test_exceptiongroup_short_summary_info.py::test_base - "
1744+
"[in BaseExceptionGroup] SystemExit('aaaaaaaaaa')"
1745+
),
1746+
(
1747+
"FAILED test_exceptiongroup_short_summary_info.py::test_nonbase - "
1748+
"[in ExceptionGroup] ValueError('aaaaaaaaaa')"
1749+
),
1750+
(
1751+
"FAILED test_exceptiongroup_short_summary_info.py::test_nested - "
1752+
"[in ExceptionGroup] ValueError('aaaaaaaaaa')"
1753+
),
1754+
(
1755+
"FAILED test_exceptiongroup_short_summary_info.py::test_multiple - "
1756+
"ExceptionGroup: bbbbbbbbbb (2 sub-exceptions)"
1757+
),
1758+
"*= 4 failed in *",
1759+
]
1760+
)
1761+
1762+
17061763
@pytest.mark.parametrize("tbstyle", ("long", "short", "auto", "line", "native"))
17071764
def test_all_entries_hidden(pytester: Pytester, tbstyle: str) -> None:
17081765
"""Regression test for #10903."""

0 commit comments

Comments
 (0)