Skip to content
Open
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/12175.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed :meth:`ExceptionInfo.exconly(tryshort=True) <ExceptionInfo.exconly>` not stripping the ``AssertionError`` prefix when the :class:`ExceptionInfo` was created via :meth:`ExceptionInfo.for_later` (e.g. when using :func:`pytest.raises`).
23 changes: 17 additions & 6 deletions src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,20 @@ def from_exception(
exc_info = (type(exception), exception, exception.__traceback__)
return cls.from_exc_info(exc_info, exprinfo)

@classmethod
def _compute_striptext(
cls,
exc_info: tuple[type[E], E, TracebackType],
) -> str:
"""Determine if AssertionError prefix should be stripped from output."""
if isinstance(exc_info[1], AssertionError):
exprinfo = getattr(exc_info[1], "msg", None)
if exprinfo is None:
exprinfo = saferepr(exc_info[1])
if exprinfo and exprinfo.startswith(cls._assert_start_repr):
return "AssertionError: "
return ""

@classmethod
def from_exc_info(
cls,
Expand All @@ -553,12 +567,8 @@ def from_exc_info(
) -> ExceptionInfo[E]:
"""Like :func:`from_exception`, but using old-style exc_info tuple."""
_striptext = ""
if exprinfo is None and isinstance(exc_info[1], AssertionError):
exprinfo = getattr(exc_info[1], "msg", None)
if exprinfo is None:
exprinfo = saferepr(exc_info[1])
if exprinfo and exprinfo.startswith(cls._assert_start_repr):
_striptext = "AssertionError: "
if exprinfo is None:
_striptext = cls._compute_striptext(exc_info)

return cls(exc_info, _striptext, _ispytest=True)

Expand Down Expand Up @@ -591,6 +601,7 @@ def fill_unfilled(self, exc_info: tuple[type[E], E, TracebackType]) -> None:
"""Fill an unfilled ExceptionInfo created with ``for_later()``."""
assert self._excinfo is None, "ExceptionInfo was already filled"
self._excinfo = exc_info
self._striptext = self._compute_striptext(exc_info)

@property
def type(self) -> type[E]:
Expand Down
45 changes: 45 additions & 0 deletions testing/code/test_excinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,51 @@ def test_excinfo_for_later() -> None:
assert "for raises" in str(e)


def test_excinfo_for_later_strips_assertion(pytester: Pytester) -> None:
"""ExceptionInfo created via for_later() should strip AssertionError prefix
with exconly(tryshort=True) for rewritten assertions (#12175)."""
pytester.makepyfile(
"""
import pytest

def test_tryshort():
with pytest.raises(AssertionError) as exc_info:
assert 1 == 2
# tryshort should strip 'AssertionError: ' from rewritten assertions
result = exc_info.exconly(tryshort=True)
assert not result.startswith("AssertionError"), (
f"Expected stripped prefix, got: {result!r}"
)
"""
)
result = pytester.runpytest()
result.assert_outcomes(passed=1)


def test_excinfo_for_later_no_strip_non_assertion() -> None:
"""fill_unfilled() should not strip prefix for non-AssertionError exceptions."""
excinfo: ExceptionInfo[ValueError] = ExceptionInfo.for_later()
try:
raise ValueError("test error")
except ValueError:
excinfo.fill_unfilled(sys.exc_info()) # type: ignore[arg-type]
assert excinfo.exconly(tryshort=True).startswith("ValueError")


def test_excinfo_for_later_strips_manual_assertion() -> None:
"""fill_unfilled() handles AssertionError with explicit .msg attribute."""
excinfo: ExceptionInfo[AssertionError] = ExceptionInfo.for_later()
try:
err = AssertionError("manual error")
err.msg = "assert something" # type: ignore[attr-defined]
raise err
except AssertionError:
excinfo.fill_unfilled(sys.exc_info()) # type: ignore[arg-type]
# Manual .msg that doesn't match _assert_start_repr should not strip
result = excinfo.exconly(tryshort=True)
assert result.startswith("AssertionError")


def test_excinfo_errisinstance():
excinfo = pytest.raises(ValueError, h)
assert excinfo.errisinstance(ValueError)
Expand Down