Skip to content

Commit

Permalink
Fix bug in Pytest 5.4.x that was altering order of output lines.
Browse files Browse the repository at this point in the history
Under pytest 5.4.x, failure reporting behavior considers all lines
without a fail_marker prefix ("E") as a line of source code. These
"source_lines" are grouped together regardless of their original
position. Doing this can scramble the output when lines without
a fail_marker appear after lines with a fail_marker. Normally,
this causes no problems but when datatest removes fail_markers
for "data differences", the output can be displayed out-of-order.

The source of this behavior was introduced in pytest-dev/pytest@4209ad6
when addressing pytest-dev/pytest#6658. You can see the diff on GitHub:

  pytest-dev/pytest@4209ad6?branch=4209ad6fcacb679d953f3dd6be96f330139006f0&diff=split#diff-ac2099a172465905236568f8c175328fb75ebc1b80ce39f4799e61373d355418R1042

In later versions of Pytest, all lines after the first fail_marker
are considered part of the error and the order of these lines is
not altered--THIS IS THE DESIRED BEHAVIOR. See Pytest 6.1.1 source
on GitHub:

  https://github.com/pytest-dev/pytest/blob/0ad20b533ffc52ced7bb1a03fba664615e90c093/src/_pytest/_code/code.py#L1059
  • Loading branch information
shawnbrown committed Oct 13, 2020
1 parent 2a4779b commit 052fb4f
Showing 1 changed file with 50 additions and 2 deletions.
52 changes: 50 additions & 2 deletions pytest_datatest.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

import itertools
import re
from _pytest._code.code import ReprEntry
from _pytest._code.code import ReprEntry as _ReprEntry
from _pytest._code.code import FormattedExcinfo
from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionRepr
Expand All @@ -40,14 +40,17 @@
from _pytest.assertion.truncate import DEFAULT_MAX_CHARS
from _pytest.assertion.truncate import USAGE_MSG
from pytest import hookimpl
from pytest import __version__ as _pytest_version
from datatest import ValidationError


PYTEST54 = _pytest_version[:3] == '5.4'

if __name__ == 'pytest_datatest':
from datatest._pytest_plugin import version_info as _bundled_version_info
else:
_bundled_version_info = (0, 0, 0)


version = '0.1.4.dev0'
version_info = (0, 1, 4)

Expand Down Expand Up @@ -146,6 +149,47 @@ def _formatted_lines_generator(lines, fail_index):
yield line


if PYTEST54:
class ReprEntry(_ReprEntry):
"""Custom ReprEntry--USE ONLY WITH PYTEST 5.4.X VERSIONS."""
def __init__(self, reprentry):
self.lines = reprentry.lines
self.reprfuncargs = reprentry.reprfuncargs
self.reprlocals = reprentry.reprlocals
self.reprfileloc = reprentry.reprfileloc
self.style = reprentry.style

def _write_entry_lines(self, tw):
"""This method is adapted from Pytest version 6.1.1."""

if not self.lines:
return

fail_marker = "{} ".format(FormattedExcinfo.fail_marker)
indent_size = len(fail_marker)
indents = []
source_lines = []
failure_lines = []
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:])

tw._write_source(source_lines, indents)

# failure lines are always completely red and bold
for line in failure_lines:
tw.line(line, bold=True, red=True)


def _format_reprtraceback(reprtraceback):
for reprentry in reprtraceback.reprentries:
try:
Expand All @@ -163,6 +207,10 @@ def _format_reprtraceback(reprtraceback):
lines = _formatted_lines_generator(lines, position)
reprentry['lines'] = list(lines)

if PYTEST54:
reprtraceback.reprentries = \
[ReprEntry(entry) for entry in reprtraceback.reprentries]


def pytest_runtest_logreport(report):
"""Hook to format the ReprEntry lines for ValidationErrors"""
Expand Down

0 comments on commit 052fb4f

Please sign in to comment.