From 1b88f543ba3804b3ae1c2b6ae7f3970897f19d39 Mon Sep 17 00:00:00 2001 From: Guillaume Marty Date: Mon, 19 Oct 2026 09:06:42 +0200 Subject: [PATCH 1/2] Trim short test summary lines to the terminal width with color enabled _get_line_with_reprcrash_message() measured the "FAILED " prefix with wcswidth() after markup was applied. The ANSI escape codes are control characters, so wcswidth() returned -1 and the message was trimmed to the full terminal width plus one column, making every colored summary line overflow by the width of its prefix. Strip the escape sequences before measuring the prefix. --- AUTHORS | 1 + changelog/PLACEHOLDER.bugfix.rst | 1 + src/_pytest/terminal.py | 7 ++++++- testing/test_terminal.py | 22 ++++++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changelog/PLACEHOLDER.bugfix.rst diff --git a/AUTHORS b/AUTHORS index bd1e27cb37d..71df6064dfd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -204,6 +204,7 @@ Gregory Lee Grig Gheorghiu Grigorii Eremeev (budulianin) Guido Wesdorp +Guillaume Marty Guoqiang Zhang hfycium Hamza Mobeen diff --git a/changelog/PLACEHOLDER.bugfix.rst b/changelog/PLACEHOLDER.bugfix.rst new file mode 100644 index 00000000000..43aca283bf2 --- /dev/null +++ b/changelog/PLACEHOLDER.bugfix.rst @@ -0,0 +1 @@ +The lines of the "short test summary info" section are now trimmed to the terminal width when color output is enabled -- previously the color escape codes broke the width computation, so the lines overflowed the terminal. diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index b77a649d28a..9aa886c5421 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -18,6 +18,7 @@ import inspect from pathlib import Path import platform +import re import sys import textwrap from typing import Any @@ -1571,6 +1572,10 @@ def _format_trimmed(format: str, msg: str, available_width: int) -> str | None: return format.format(msg) +# Escape sequences added by TerminalWriter.markup(), which take no columns. +_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]*m") + + def _get_line_with_reprcrash_message( config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: dict[str, bool] ) -> str: @@ -1582,7 +1587,7 @@ def _get_line_with_reprcrash_message( node = _get_node_id_with_markup(tw, config, rep) line = f"{word} {node}" - line_width = wcswidth(line) + line_width = wcswidth(_ANSI_ESCAPE_SEQ.sub("", line)) msg: str | None try: diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 21b0557a470..d0421ba29f8 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -6,6 +6,7 @@ from io import StringIO import os from pathlib import Path +import re import sys import textwrap from types import SimpleNamespace @@ -1409,6 +1410,27 @@ def test_fail_extra_reporting( ) +def test_fail_extra_reporting_truncation_with_color( + pytester: Pytester, monkeypatch: MonkeyPatch +) -> None: + """The short summary line is trimmed to the terminal width also when + color is enabled: the escape codes of the markup take no columns.""" + monkeypatch.delenv("CI", raising=False) + monkeypatch.setenv("COLUMNS", "80") + pytester.makepyfile("def test_this(): assert 0, 'this_failed' * 100") + + def summary_lines(*args: str) -> list[str]: + result = pytester.runpytest(*args) + lines = [re.sub(r"\x1b\[[\d;]*m", "", line) for line in result.stdout.lines] + return [line for line in lines if line.startswith("FAILED ")] + + expected = summary_lines("--color=no") + assert len(expected) == 1 + assert expected[0].endswith(" - Assert...") + assert len(expected[0]) == 80 + assert summary_lines("--color=yes") == expected + + def test_fail_reporting_on_pass(pytester: Pytester) -> None: pytester.makepyfile("def test_this(): assert 1") result = pytester.runpytest("-rf") From f5ce1f2142050e3b7d0a428378c8dbea1d9df89d Mon Sep 17 00:00:00 2001 From: Guillaume Marty Date: Mon, 19 Oct 2026 10:22:11 +0200 Subject: [PATCH 2/2] Rename changelog fragment to PR number Co-authored-by: Claude --- changelog/{PLACEHOLDER.bugfix.rst => 15216.bugfix.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{PLACEHOLDER.bugfix.rst => 15216.bugfix.rst} (100%) diff --git a/changelog/PLACEHOLDER.bugfix.rst b/changelog/15216.bugfix.rst similarity index 100% rename from changelog/PLACEHOLDER.bugfix.rst rename to changelog/15216.bugfix.rst