Skip to content

Commit

Permalink
Fix handle of exceptions in ReprEntry with tb=line
Browse files Browse the repository at this point in the history
Fix #7707
  • Loading branch information
nicoddemus committed Sep 3, 2020
1 parent 91dbdb6 commit 51a7d44
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 31 deletions.
1 change: 1 addition & 0 deletions changelog/7707.bugfix.rst
@@ -0,0 +1 @@
Fix internal error when handling some exceptions that contain multiple lines or the style uses multiple lines (``--tb=line`` for example).
24 changes: 10 additions & 14 deletions src/_pytest/_code/code.py
Expand Up @@ -1049,25 +1049,21 @@ def _write_entry_lines(self, tw: TerminalWriter) -> None:
# such as "> assert 0"
fail_marker = "{} ".format(FormattedExcinfo.fail_marker)
indent_size = len(fail_marker)
indents = []
source_lines = []
failure_lines = []
seeing_failures = False
for line in self.lines:
is_source_line = not line.startswith(fail_marker)
if is_source_line:
assert not seeing_failures, (
"Unexpected failure lines between source lines:\n"
+ "\n".join(self.lines)
)
indents: List[str] = []
source_lines: List[str] = []
failure_lines: List[str] = []
for index, line in enumerate(self.lines):
is_failure_line = line.startswith(fail_marker)
if is_failure_line:
# from this point on all lines are considered part of the failure
failure_lines.extend(self.lines[index:])
break
else:
if self.style == "value":
source_lines.append(line)
else:
indents.append(line[:indent_size])
source_lines.append(line[indent_size:])
else:
seeing_failures = True
failure_lines.append(line)

tw._write_source(source_lines, indents)

Expand Down
42 changes: 25 additions & 17 deletions testing/code/test_excinfo.py
Expand Up @@ -4,6 +4,8 @@
import queue
import sys
import textwrap
from typing import Any
from typing import Dict
from typing import Tuple
from typing import Union

Expand Down Expand Up @@ -1045,28 +1047,34 @@ def f():
@pytest.mark.parametrize(
"reproptions",
[
{
"style": style,
"showlocals": showlocals,
"funcargs": funcargs,
"tbfilter": tbfilter,
}
for style in ("long", "short", "no")
pytest.param(
{
"style": style,
"showlocals": showlocals,
"funcargs": funcargs,
"tbfilter": tbfilter,
},
id="style={},showlocals={},funcargs={},tbfilter={}".format(
style, showlocals, funcargs, tbfilter
),
)
for style in ["long", "short", "line", "no", "native", "value", "auto"]
for showlocals in (True, False)
for tbfilter in (True, False)
for funcargs in (True, False)
],
)
def test_format_excinfo(self, importasmod, reproptions):
mod = importasmod(
"""
def g(x):
raise ValueError(x)
def f():
g(3)
"""
)
excinfo = pytest.raises(ValueError, mod.f)
def test_format_excinfo(self, reproptions: Dict[str, Any]) -> None:
def bar():
assert False, "some error"

def foo():
bar()

# using inline functions as opposed to importasmod so we get source code lines
# in the tracebacks (otherwise getinspect doesn't find the source code).
with pytest.raises(AssertionError) as excinfo:
foo()
file = io.StringIO()
tw = TerminalWriter(file=file)
repr = excinfo.getrepr(**reproptions)
Expand Down

0 comments on commit 51a7d44

Please sign in to comment.