From c1c5a2b34ad59e778d6cdc022ed03039375478c3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 10 Jul 2020 14:49:10 +0300 Subject: [PATCH] Add support for NO_COLOR and FORCE_COLOR (#7466) Co-authored-by: Bruno Oliveira --- changelog/7464.feature.rst | 3 +++ doc/en/reference.rst | 29 ++++++++++++++++++++------ src/_pytest/_io/terminalwriter.py | 9 ++++---- testing/io/test_terminalwriter.py | 34 +++++++++++++++++++++++++++---- 4 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 changelog/7464.feature.rst diff --git a/changelog/7464.feature.rst b/changelog/7464.feature.rst new file mode 100644 index 00000000000..db9d3c60415 --- /dev/null +++ b/changelog/7464.feature.rst @@ -0,0 +1,3 @@ +Added support for ``NO_COLOR`` and ``FORCE_COLOR`` environment variables to control colored output. + +For more information, see `the docs `__. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 94a2470953a..86ed89d897a 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -988,10 +988,20 @@ Environment variables that can be used to change pytest's behavior. This contains a command-line (parsed by the py:mod:`shlex` module) that will be **prepended** to the command line given by the user, see :ref:`adding default options` for more information. +.. envvar:: PYTEST_CURRENT_TEST + +This is not meant to be set by users, but is set by pytest internally with the name of the current test so other +processes can inspect it, see :ref:`pytest current test env` for more information. + .. envvar:: PYTEST_DEBUG When set, pytest will print tracing and debug information. +.. envvar:: PYTEST_DISABLE_PLUGIN_AUTOLOAD + +When set, disables plugin auto-loading through setuptools entrypoints. Only explicitly specified plugins will be +loaded. + .. envvar:: PYTEST_PLUGINS Contains comma-separated list of modules that should be loaded as plugins: @@ -1000,15 +1010,22 @@ Contains comma-separated list of modules that should be loaded as plugins: export PYTEST_PLUGINS=mymodule.plugin,xdist -.. envvar:: PYTEST_DISABLE_PLUGIN_AUTOLOAD +.. envvar:: PY_COLORS -When set, disables plugin auto-loading through setuptools entrypoints. Only explicitly specified plugins will be -loaded. +When set to ``1``, pytest will use color in terminal output. +When set to ``0``, pytest will not use color. +``PY_COLORS`` takes precedence over ``NO_COLOR`` and ``FORCE_COLOR``. -.. envvar:: PYTEST_CURRENT_TEST +.. envvar:: NO_COLOR -This is not meant to be set by users, but is set by pytest internally with the name of the current test so other -processes can inspect it, see :ref:`pytest current test env` for more information. +When set (regardless of value), pytest will not use color in terminal output. +``PY_COLORS`` takes precedence over ``NO_COLOR``, which takes precedence over ``FORCE_COLOR``. +See `no-color.org `__ for other libraries supporting this community standard. + +.. envvar:: FORCE_COLOR + +When set (regardless of value), pytest will use color in terminal output. +``PY_COLORS`` and ``NO_COLOR`` take precedence over ``FORCE_COLOR``. Exceptions ---------- diff --git a/src/_pytest/_io/terminalwriter.py b/src/_pytest/_io/terminalwriter.py index 70bb2e2dcd6..0168dc13d4d 100644 --- a/src/_pytest/_io/terminalwriter.py +++ b/src/_pytest/_io/terminalwriter.py @@ -27,11 +27,12 @@ def should_do_markup(file: TextIO) -> bool: return True if os.environ.get("PY_COLORS") == "0": return False + if "NO_COLOR" in os.environ: + return False + if "FORCE_COLOR" in os.environ: + return True return ( - hasattr(file, "isatty") - and file.isatty() - and os.environ.get("TERM") != "dumb" - and not (sys.platform.startswith("java") and os._name == "nt") + hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb" ) diff --git a/testing/io/test_terminalwriter.py b/testing/io/test_terminalwriter.py index 94cff307fcd..b36a7bb6a11 100644 --- a/testing/io/test_terminalwriter.py +++ b/testing/io/test_terminalwriter.py @@ -154,8 +154,7 @@ def test_attr_hasmarkup() -> None: assert "\x1b[0m" in s -def test_should_do_markup_PY_COLORS_eq_1(monkeypatch: MonkeyPatch) -> None: - monkeypatch.setitem(os.environ, "PY_COLORS", "1") +def assert_color_set(): file = io.StringIO() tw = terminalwriter.TerminalWriter(file) assert tw.hasmarkup @@ -166,8 +165,7 @@ def test_should_do_markup_PY_COLORS_eq_1(monkeypatch: MonkeyPatch) -> None: assert "\x1b[0m" in s -def test_should_do_markup_PY_COLORS_eq_0(monkeypatch: MonkeyPatch) -> None: - monkeypatch.setitem(os.environ, "PY_COLORS", "0") +def assert_color_not_set(): f = io.StringIO() f.isatty = lambda: True # type: ignore tw = terminalwriter.TerminalWriter(file=f) @@ -177,6 +175,34 @@ def test_should_do_markup_PY_COLORS_eq_0(monkeypatch: MonkeyPatch) -> None: assert s == "hello\n" +def test_should_do_markup_PY_COLORS_eq_1(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setitem(os.environ, "PY_COLORS", "1") + assert_color_set() + + +def test_should_not_do_markup_PY_COLORS_eq_0(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setitem(os.environ, "PY_COLORS", "0") + assert_color_not_set() + + +def test_should_not_do_markup_NO_COLOR(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setitem(os.environ, "NO_COLOR", "1") + assert_color_not_set() + + +def test_should_do_markup_FORCE_COLOR(monkeypatch: MonkeyPatch) -> None: + monkeypatch.setitem(os.environ, "FORCE_COLOR", "1") + assert_color_set() + + +def test_should_not_do_markup_NO_COLOR_and_FORCE_COLOR( + monkeypatch: MonkeyPatch, +) -> None: + monkeypatch.setitem(os.environ, "NO_COLOR", "1") + monkeypatch.setitem(os.environ, "FORCE_COLOR", "1") + assert_color_not_set() + + class TestTerminalWriterLineWidth: def test_init(self) -> None: tw = terminalwriter.TerminalWriter()