From 76e044477010dcc0e31d4a736b7130e044a01a7e Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Mon, 25 Nov 2024 19:25:50 +0700 Subject: [PATCH] pytest.fail: fix ANSI escape codes for colored output (#12959) - When `ReprEntry.style == "value"` (happens when calling `pytest.fail(..., pytrace=False)`, the message should not be printed to terminal using `TerminalWriter._write_source` because then it'll try to highlight the message as source code - The message should be printed to terminal directly using `TerminalWriter.line` or `TerminalWriter.write`, I went with the later for testing purposes https://github.com/pytest-dev/pytest/pull/12959#discussion_r1842574618 Closes #12849 --- AUTHORS | 1 + changelog/12849.bugfix.rst | 1 + src/_pytest/_code/code.py | 16 +++++++++++----- testing/code/test_excinfo.py | 17 +++++++++++++++++ testing/conftest.py | 4 ++-- 5 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 changelog/12849.bugfix.rst diff --git a/AUTHORS b/AUTHORS index c38f74d9980..303d04133cb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -247,6 +247,7 @@ Kristoffer Nordström Kyle Altendorf Lawrence Mitchell Lee Kamentsky +Leonardus Chen Lev Maximov Levon Saldamli Lewis Cowles diff --git a/changelog/12849.bugfix.rst b/changelog/12849.bugfix.rst new file mode 100644 index 00000000000..fb72263aadd --- /dev/null +++ b/changelog/12849.bugfix.rst @@ -0,0 +1 @@ +ANSI escape codes for colored output now handled correctly in :func:`pytest.fail` with `pytrace=False`. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 8fac39ea298..fec627b3a36 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -1221,6 +1221,15 @@ def _write_entry_lines(self, tw: TerminalWriter) -> None: if not self.lines: return + if self.style == "value": + # Using tw.write instead of tw.line for testing purposes due to TWMock implementation; + # lines written with TWMock.line and TWMock._write_source cannot be distinguished + # from each other, whereas lines written with TWMock.write are marked with TWMock.WRITE + for line in self.lines: + tw.write(line) + tw.write("\n") + return + # separate indents and source lines that are not failures: we want to # highlight the code but not the indentation, which may contain markers # such as "> assert 0" @@ -1236,11 +1245,8 @@ def _write_entry_lines(self, tw: TerminalWriter) -> None: 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:]) + indents.append(line[:indent_size]) + source_lines.append(line[indent_size:]) tw._write_source(source_lines, indents) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index fc60ae9ac99..97c207e9795 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1194,6 +1194,23 @@ def f(): line = tw_mock.lines[-1] assert line == ":3: ValueError" + def test_toterminal_value(self, importasmod, tw_mock): + mod = importasmod( + """ + def g(x): + raise ValueError(x) + def f(): + g('some_value') + """ + ) + excinfo = pytest.raises(ValueError, mod.f) + excinfo.traceback = excinfo.traceback.filter(excinfo) + repr = excinfo.getrepr(style="value") + repr.toterminal(tw_mock) + + assert tw_mock.get_write_msg(0) == "some_value" + assert tw_mock.get_write_msg(1) == "\n" + @pytest.mark.parametrize( "reproptions", [ diff --git a/testing/conftest.py b/testing/conftest.py index 046bb77a109..69af03324d6 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -119,8 +119,8 @@ def markup(self, text, **kw): return text def get_write_msg(self, idx): - flag, msg = self.lines[idx] - assert flag == TWMock.WRITE + assert self.lines[idx][0] == TWMock.WRITE + msg = self.lines[idx][1] return msg fullwidth = 80