[7.4.x] Fix handling empty values of NO_COLOR and FORCE_COLOR

This commit is contained in:
Michał Górny 2023-12-23 09:12:13 +01:00 committed by pytest bot
parent 2cdd619bf4
commit 31a59cf499
5 changed files with 45 additions and 28 deletions

View File

@ -260,6 +260,7 @@ Michael Goerz
Michael Krebs Michael Krebs
Michael Seifert Michael Seifert
Michal Wajszczuk Michal Wajszczuk
Michał Górny
Michał Zięba Michał Zięba
Mickey Pashov Mickey Pashov
Mihai Capotă Mihai Capotă

View File

@ -0,0 +1 @@
Fixed handling ``NO_COLOR`` and ``FORCE_COLOR`` to ignore an empty value.

View File

@ -1122,13 +1122,13 @@ When set to ``0``, pytest will not use color.
.. envvar:: NO_COLOR .. envvar:: NO_COLOR
When set (regardless of value), pytest will not use color in terminal output. When set to a non-empty string (regardless of value), pytest will not use color in terminal output.
``PY_COLORS`` takes precedence over ``NO_COLOR``, which takes precedence over ``FORCE_COLOR``. ``PY_COLORS`` takes precedence over ``NO_COLOR``, which takes precedence over ``FORCE_COLOR``.
See `no-color.org <https://no-color.org/>`__ for other libraries supporting this community standard. See `no-color.org <https://no-color.org/>`__ for other libraries supporting this community standard.
.. envvar:: FORCE_COLOR .. envvar:: FORCE_COLOR
When set (regardless of value), pytest will use color in terminal output. When set to a non-empty string (regardless of value), pytest will use color in terminal output.
``PY_COLORS`` and ``NO_COLOR`` take precedence over ``FORCE_COLOR``. ``PY_COLORS`` and ``NO_COLOR`` take precedence over ``FORCE_COLOR``.
Exceptions Exceptions

View File

@ -28,9 +28,9 @@ def should_do_markup(file: TextIO) -> bool:
return True return True
if os.environ.get("PY_COLORS") == "0": if os.environ.get("PY_COLORS") == "0":
return False return False
if "NO_COLOR" in os.environ: if os.environ.get("NO_COLOR"):
return False return False
if "FORCE_COLOR" in os.environ: if os.environ.get("FORCE_COLOR"):
return True return True
return ( return (
hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb" hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb"

View File

@ -5,6 +5,7 @@ import shutil
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Generator from typing import Generator
from typing import Optional
from unittest import mock from unittest import mock
import pytest import pytest
@ -164,53 +165,67 @@ def test_attr_hasmarkup() -> None:
assert "\x1b[0m" in s assert "\x1b[0m" in s
def assert_color_set(): def assert_color(expected: bool, default: Optional[bool] = None) -> None:
file = io.StringIO() file = io.StringIO()
tw = terminalwriter.TerminalWriter(file) if default is None:
assert tw.hasmarkup default = not expected
file.isatty = lambda: default # type: ignore
tw = terminalwriter.TerminalWriter(file=file)
assert tw.hasmarkup is expected
tw.line("hello", bold=True) tw.line("hello", bold=True)
s = file.getvalue() s = file.getvalue()
if expected:
assert len(s) > len("hello\n") assert len(s) > len("hello\n")
assert "\x1b[1m" in s assert "\x1b[1m" in s
assert "\x1b[0m" in s assert "\x1b[0m" in s
else:
def assert_color_not_set():
f = io.StringIO()
f.isatty = lambda: True # type: ignore
tw = terminalwriter.TerminalWriter(file=f)
assert not tw.hasmarkup
tw.line("hello", bold=True)
s = f.getvalue()
assert s == "hello\n" assert s == "hello\n"
def test_should_do_markup_PY_COLORS_eq_1(monkeypatch: MonkeyPatch) -> None: def test_should_do_markup_PY_COLORS_eq_1(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setitem(os.environ, "PY_COLORS", "1") monkeypatch.setitem(os.environ, "PY_COLORS", "1")
assert_color_set() assert_color(True)
def test_should_not_do_markup_PY_COLORS_eq_0(monkeypatch: MonkeyPatch) -> None: def test_should_not_do_markup_PY_COLORS_eq_0(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setitem(os.environ, "PY_COLORS", "0") monkeypatch.setitem(os.environ, "PY_COLORS", "0")
assert_color_not_set() assert_color(False)
def test_should_not_do_markup_NO_COLOR(monkeypatch: MonkeyPatch) -> None: def test_should_not_do_markup_NO_COLOR(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setitem(os.environ, "NO_COLOR", "1") monkeypatch.setitem(os.environ, "NO_COLOR", "1")
assert_color_not_set() assert_color(False)
def test_should_do_markup_FORCE_COLOR(monkeypatch: MonkeyPatch) -> None: def test_should_do_markup_FORCE_COLOR(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setitem(os.environ, "FORCE_COLOR", "1") monkeypatch.setitem(os.environ, "FORCE_COLOR", "1")
assert_color_set() assert_color(True)
def test_should_not_do_markup_NO_COLOR_and_FORCE_COLOR( @pytest.mark.parametrize(
["NO_COLOR", "FORCE_COLOR", "expected"],
[
("1", "1", False),
("", "1", True),
("1", "", False),
],
)
def test_NO_COLOR_and_FORCE_COLOR(
monkeypatch: MonkeyPatch, monkeypatch: MonkeyPatch,
NO_COLOR: str,
FORCE_COLOR: str,
expected: bool,
) -> None: ) -> None:
monkeypatch.setitem(os.environ, "NO_COLOR", "1") monkeypatch.setitem(os.environ, "NO_COLOR", NO_COLOR)
monkeypatch.setitem(os.environ, "FORCE_COLOR", "1") monkeypatch.setitem(os.environ, "FORCE_COLOR", FORCE_COLOR)
assert_color_not_set() assert_color(expected)
def test_empty_NO_COLOR_and_FORCE_COLOR_ignored(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setitem(os.environ, "NO_COLOR", "")
monkeypatch.setitem(os.environ, "FORCE_COLOR", "")
assert_color(True, True)
assert_color(False, False)
class TestTerminalWriterLineWidth: class TestTerminalWriterLineWidth: