Skip to content

Commit

Permalink
Merge pull request #12975 from jakkdl/short_info_excgroup
Browse files Browse the repository at this point in the history
display single contained exception in excgroups in test summary
  • Loading branch information
Zac-HD authored Nov 25, 2024
2 parents 2157caf + 6f61360 commit acf1303
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/12943.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +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.
17 changes: 17 additions & 0 deletions src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,23 @@ def exconly(self, tryshort: bool = False) -> str:
representation is returned (so 'AssertionError: ' is removed from
the beginning).
"""

def _get_single_subexc(
eg: BaseExceptionGroup[BaseException],
) -> BaseException | None:
if len(eg.exceptions) != 1:
return None
if isinstance(e := eg.exceptions[0], BaseExceptionGroup):
return _get_single_subexc(e)
return e

if (
tryshort
and isinstance(self.value, BaseExceptionGroup)
and (subexc := _get_single_subexc(self.value)) is not None
):
return f"{subexc!r} [single exception in {type(self.value).__name__}]"

lines = format_exception_only(self.type, self.value)
text = "".join(lines)
text = text.rstrip()
Expand Down
77 changes: 77 additions & 0 deletions testing/code/test_excinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,83 @@ def test_exceptiongroup(pytester: Pytester, outer_chain, inner_chain) -> None:
_exceptiongroup_common(pytester, outer_chain, inner_chain, native=False)


def test_exceptiongroup_short_summary_info(pytester: Pytester):
pytester.makepyfile(
"""
import sys
if sys.version_info < (3, 11):
from exceptiongroup import BaseExceptionGroup, ExceptionGroup
def test_base() -> None:
raise BaseExceptionGroup("NOT IN SUMMARY", [SystemExit("a" * 10)])
def test_nonbase() -> None:
raise ExceptionGroup("NOT IN SUMMARY", [ValueError("a" * 10)])
def test_nested() -> None:
raise ExceptionGroup(
"NOT DISPLAYED", [
ExceptionGroup("NOT IN SUMMARY", [ValueError("a" * 10)])
]
)
def test_multiple() -> None:
raise ExceptionGroup(
"b" * 10,
[
ValueError("NOT IN SUMMARY"),
TypeError("NOT IN SUMMARY"),
]
)
def test_nested_multiple() -> None:
raise ExceptionGroup(
"b" * 10,
[
ExceptionGroup(
"c" * 10,
[
ValueError("NOT IN SUMMARY"),
TypeError("NOT IN SUMMARY"),
]
)
]
)
"""
)
# run with -vv to not truncate summary info, default width in tests is very low
result = pytester.runpytest("-vv")
assert result.ret == 1
backport_str = "exceptiongroup." if sys.version_info < (3, 11) else ""
result.stdout.fnmatch_lines(
[
"*= short test summary info =*",
(
"FAILED test_exceptiongroup_short_summary_info.py::test_base - "
"SystemExit('aaaaaaaaaa') [single exception in BaseExceptionGroup]"
),
(
"FAILED test_exceptiongroup_short_summary_info.py::test_nonbase - "
"ValueError('aaaaaaaaaa') [single exception in ExceptionGroup]"
),
(
"FAILED test_exceptiongroup_short_summary_info.py::test_nested - "
"ValueError('aaaaaaaaaa') [single exception in ExceptionGroup]"
),
(
"FAILED test_exceptiongroup_short_summary_info.py::test_multiple - "
f"{backport_str}ExceptionGroup: bbbbbbbbbb (2 sub-exceptions)"
),
(
"FAILED test_exceptiongroup_short_summary_info.py::test_nested_multiple - "
f"{backport_str}ExceptionGroup: bbbbbbbbbb (1 sub-exception)"
),
"*= 5 failed in *",
]
)


@pytest.mark.parametrize("tbstyle", ("long", "short", "auto", "line", "native"))
def test_all_entries_hidden(pytester: Pytester, tbstyle: str) -> None:
"""Regression test for #10903."""
Expand Down

0 comments on commit acf1303

Please sign in to comment.