Skip to content

Commit 94c7b8b

Browse files
Issue 1316 - longrepr is a string when pytrace=False (#7100)
1 parent e319060 commit 94c7b8b

File tree

6 files changed

+44
-20
lines changed

6 files changed

+44
-20
lines changed

changelog/1316.breaking.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``TestReport.longrepr`` is now always an instance of ``ReprExceptionInfo``. Previously it was a ``str`` when a test failed with ``pytest.fail(..., pytrace=False)``.

src/_pytest/_code/code.py

+29-15
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
from typing_extensions import Literal
4747
from weakref import ReferenceType
4848

49-
_TracebackStyle = Literal["long", "short", "line", "no", "native"]
49+
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value"]
5050

5151

5252
class Code:
@@ -583,7 +583,7 @@ def getrepr(
583583
Show locals per traceback entry.
584584
Ignored if ``style=="native"``.
585585
586-
:param str style: long|short|no|native traceback style
586+
:param str style: long|short|no|native|value traceback style
587587
588588
:param bool abspath:
589589
If paths should be changed to absolute or left unchanged.
@@ -758,16 +758,15 @@ def repr_locals(self, locals: Dict[str, object]) -> Optional["ReprLocals"]:
758758
def repr_traceback_entry(
759759
self, entry: TracebackEntry, excinfo: Optional[ExceptionInfo] = None
760760
) -> "ReprEntry":
761-
source = self._getentrysource(entry)
762-
if source is None:
763-
source = Source("???")
764-
line_index = 0
765-
else:
766-
line_index = entry.lineno - entry.getfirstlinesource()
767-
768761
lines = [] # type: List[str]
769762
style = entry._repr_style if entry._repr_style is not None else self.style
770763
if style in ("short", "long"):
764+
source = self._getentrysource(entry)
765+
if source is None:
766+
source = Source("???")
767+
line_index = 0
768+
else:
769+
line_index = entry.lineno - entry.getfirstlinesource()
771770
short = style == "short"
772771
reprargs = self.repr_args(entry) if not short else None
773772
s = self.get_source(source, line_index, excinfo, short=short)
@@ -780,9 +779,14 @@ def repr_traceback_entry(
780779
reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
781780
localsrepr = self.repr_locals(entry.locals)
782781
return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
783-
if excinfo:
784-
lines.extend(self.get_exconly(excinfo, indent=4))
785-
return ReprEntry(lines, None, None, None, style)
782+
elif style == "value":
783+
if excinfo:
784+
lines.extend(str(excinfo.value).split("\n"))
785+
return ReprEntry(lines, None, None, None, style)
786+
else:
787+
if excinfo:
788+
lines.extend(self.get_exconly(excinfo, indent=4))
789+
return ReprEntry(lines, None, None, None, style)
786790

787791
def _makepath(self, path):
788792
if not self.abspath:
@@ -806,6 +810,11 @@ def repr_traceback(self, excinfo: ExceptionInfo) -> "ReprTraceback":
806810

807811
last = traceback[-1]
808812
entries = []
813+
if self.style == "value":
814+
reprentry = self.repr_traceback_entry(last, excinfo)
815+
entries.append(reprentry)
816+
return ReprTraceback(entries, None, style=self.style)
817+
809818
for index, entry in enumerate(traceback):
810819
einfo = (last == entry) and excinfo or None
811820
reprentry = self.repr_traceback_entry(entry, einfo)
@@ -865,7 +874,9 @@ def repr_excinfo(self, excinfo: ExceptionInfo) -> "ExceptionChainRepr":
865874
seen.add(id(e))
866875
if excinfo_:
867876
reprtraceback = self.repr_traceback(excinfo_)
868-
reprcrash = excinfo_._getreprcrash() # type: Optional[ReprFileLocation]
877+
reprcrash = (
878+
excinfo_._getreprcrash() if self.style != "value" else None
879+
) # type: Optional[ReprFileLocation]
869880
else:
870881
# fallback to native repr if the exception doesn't have a traceback:
871882
# ExceptionInfo objects require a full traceback to work
@@ -1048,8 +1059,11 @@ def _write_entry_lines(self, tw: TerminalWriter) -> None:
10481059
"Unexpected failure lines between source lines:\n"
10491060
+ "\n".join(self.lines)
10501061
)
1051-
indents.append(line[:indent_size])
1052-
source_lines.append(line[indent_size:])
1062+
if self.style == "value":
1063+
source_lines.append(line)
1064+
else:
1065+
indents.append(line[:indent_size])
1066+
source_lines.append(line[indent_size:])
10531067
else:
10541068
seeing_failures = True
10551069
failure_lines.append(line)

src/_pytest/junitxml.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,8 @@ def append_failure(self, report):
202202
if hasattr(report, "wasxfail"):
203203
self._add_simple(Junit.skipped, "xfail-marked test passes unexpectedly")
204204
else:
205-
if hasattr(report.longrepr, "reprcrash"):
205+
if getattr(report.longrepr, "reprcrash", None) is not None:
206206
message = report.longrepr.reprcrash.message
207-
elif isinstance(report.longrepr, str):
208-
message = report.longrepr
209207
else:
210208
message = str(report.longrepr)
211209
message = bin_xml_escape(message)

src/_pytest/nodes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def _repr_failure_py(
337337
excinfo = ExceptionInfo(excinfo.value.excinfo)
338338
if isinstance(excinfo.value, fail.Exception):
339339
if not excinfo.value.pytrace:
340-
return str(excinfo.value)
340+
style = "value"
341341
if isinstance(excinfo.value, FixtureLookupError):
342342
return excinfo.value.formatrepr()
343343
if self.config.getoption("fulltrace", False):

testing/test_runner.py

+11
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,17 @@ def test_func():
10021002
assert rep.capstdout == ""
10031003
assert rep.capstderr == ""
10041004

1005+
def test_longrepr_type(self, testdir) -> None:
1006+
reports = testdir.runitem(
1007+
"""
1008+
import pytest
1009+
def test_func():
1010+
pytest.fail(pytrace=False)
1011+
"""
1012+
)
1013+
rep = reports[1]
1014+
assert isinstance(rep.longrepr, _pytest._code.code.ExceptionRepr)
1015+
10051016

10061017
def test_outcome_exception_bad_msg() -> None:
10071018
"""Check that OutcomeExceptions validate their input to prevent confusing errors (#5578)"""

testing/test_skipping.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def test_func():
194194
assert len(reports) == 3
195195
callreport = reports[1]
196196
assert callreport.failed
197-
assert callreport.longrepr == "[XPASS(strict)] nope"
197+
assert str(callreport.longrepr) == "[XPASS(strict)] nope"
198198
assert not hasattr(callreport, "wasxfail")
199199

200200
def test_xfail_run_anyway(self, testdir):

0 commit comments

Comments
 (0)