Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes issue #2606 by making color=True behavior agnostic of Operating System. #2731

Open
wants to merge 1 commit into
base: 8.1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ Unreleased
the help for an option. :issue:`2500`
- The test runner handles stripping color consistently on Windows.
:issue:`2705`

- Fix/Changed the way ``color=True`` is applied when the OS is windows,
additionally changed ``test_utils.py`` by removing the old version of
``test_echo_color_flag`` with three different versions to test multiple
cases of ``click.echo()``. :issue:`2606`

Version 8.1.7
-------------
Expand Down
2 changes: 1 addition & 1 deletion src/click/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ def echo(
out = strip_ansi(out)
elif WIN:
if auto_wrap_for_ansi is not None:
file = auto_wrap_for_ansi(file) # type: ignore
file = auto_wrap_for_ansi(file, color) # type: ignore
elif not color:
out = strip_ansi(out)

Expand Down
163 changes: 149 additions & 14 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,32 +209,167 @@ def test_echo_via_pager(monkeypatch, capfd, cat, test):
assert out == expected_output


@pytest.mark.skipif(WIN, reason="Test does not make sense on Windows.")
def test_echo_color_flag(monkeypatch, capfd):
"""
These variables are used for the next three tests.

_text_str is guranteeing that each color test is using the same
string.

_styled_text_str is running _text_str through click.style with fg
set to red. This gurantees that each test is getting the same styled text.

expected_stripped_text is using an fstring on _text_str to add a newline chracter
to the end of it.

expected_unstripped_text is using an fstring on_styled_text_str to add a newline
character to the end of it.

Each expected variable is what is being used in the asserts to gurantee the output
is correct.

The unstripped version is for when an the ANSI style should stay applied.
The stripped version is for when the ANSI style should be removed from the text.

These can be removed if you would rather have the parametrize values be explict
in the following tests. The variables could also be extended and the tests truncated
by adding isatty and jupyter_kernel variables here. Expanding the parametrize
matrix to use those within the test instead of it being explict based on tests.
"""
_text_str = "test"
_styled_text_str = click.style(_text_str, fg="red")
expected_stripped_text = f"{_text_str}\n"
expected_unstripped_text = f"{_styled_text_str}\n"


@pytest.mark.parametrize(
"color, text, stylized_text, expected",
[
(False, _text_str, _styled_text_str, expected_stripped_text),
(True, _text_str, _styled_text_str, expected_unstripped_text),
(None, _text_str, _styled_text_str, expected_unstripped_text),
],
)
def test_echo_color_flag_atty_and_not_jupyter_kernel(
monkeypatch, capfd, color, text, stylized_text, expected
):
"""
This is the test for echo color when the stream is a tty and
not a jupyter kernel output.

If color is set to False, then the text should strip the ANSI values regardless
of operating system.

If color is set to True, then we are forcing click to not care about
the type of stream or operating system. This means ANSI style codes will be applied
even if the output would not support them. Instances of this would be piping an
echo to a file.

If color is set to None / not set, then click will check if the stream is a tty.
This test is forcing that to always be true, and will then mean that the output
should keep the ANSI style.

The mocking of _is_jupyter_kernel_output is not needed, but was added to keep it
consistent with the other tests.
"""
isatty = True
monkeypatch.setattr(click._compat, "isatty", lambda x: isatty)

text = "foo"
jupyter_kernel = False
monkeypatch.setattr(
click._compat, "_is_jupyter_kernel_output", lambda x: jupyter_kernel
)

styled_text = click.style(text, fg="red")
assert styled_text == "\x1b[31mfoo\x1b[0m"
assert styled_text == stylized_text

click.echo(styled_text, color=False)
click.echo(styled_text, color=color)
out, err = capfd.readouterr()
assert out == f"{text}\n"
assert out == expected

click.echo(styled_text, color=True)
out, err = capfd.readouterr()
assert out == f"{styled_text}\n"

isatty = True
click.echo(styled_text)
@pytest.mark.parametrize(
"color, text, stylized_text, expected",
[
(False, _text_str, _styled_text_str, expected_stripped_text),
(True, _text_str, _styled_text_str, expected_unstripped_text),
(None, _text_str, _styled_text_str, expected_unstripped_text),
],
)
def test_echo_color_flag_jupyter_kernel_and_not_atty(
monkeypatch, capfd, color, text, stylized_text, expected
):
"""
This is the test for echo color when the stream is not a tty and
is a jupyter kernel output.

If color is set to False, then the text should strip the ANSI values regardless
of operating system.

If color is set to True, then we are forcing click to not care about
the type of stream or operating system. This means ANSI style codes will be applied
even if the output would not support them. Instances of this would be piping an
echo to a file.

If color is set to None / not set, then click will check if the stream is a tty.
Which is being forced to be False so, the strip function will next check if
the stream is a jupyter kernel output which is being set as True. This means
that the ANSI style should not be stripped.
"""
jupyter_kernel = True
monkeypatch.setattr(
click._compat, "_is_jupyter_kernel_output", lambda x: jupyter_kernel
)
isatty = False
monkeypatch.setattr(click._compat, "isatty", lambda x: isatty)

styled_text = click.style(text, fg="red")
assert styled_text == stylized_text

click.echo(styled_text, color=color)
out, err = capfd.readouterr()
assert out == f"{styled_text}\n"
assert out == expected


@pytest.mark.parametrize(
"color, text, stylized_text, expected",
[
(False, _text_str, _styled_text_str, expected_stripped_text),
(True, _text_str, _styled_text_str, expected_unstripped_text),
(None, _text_str, _styled_text_str, expected_stripped_text),
],
)
def test_echo_color_flag_not_atty_and_not_jupyter_kernel(
monkeypatch, capfd, color, text, stylized_text, expected
):
"""
This is the test for echo color when the stream is not a tty nor a jupyter kernel
output.

If color is set to False, then the text should strip the ANSI values regardless
of operating system.

If color is set to True, then we are forcing click to not care about
the type of stream or operating system. This means ANSI style codes will be applied
even if the output would not support them. Instances of this would be piping an
echo to a file.

If color is set to None / not set, then click will check that the stream is neither
a tty nor a jupyter notebook. This means the styling should be stripped regardless
of the operating system.
"""
isatty = False
click.echo(styled_text)
monkeypatch.setattr(click._compat, "isatty", lambda x: isatty)
jupyter_kernel = False
monkeypatch.setattr(
click._compat, "_is_jupyter_kernel_output", lambda x: jupyter_kernel
)

styled_text = click.style(text, fg="red")
assert styled_text == stylized_text

click.echo(styled_text, color=color)
out, err = capfd.readouterr()
assert out == f"{text}\n"
assert out == expected


def test_prompt_cast_default(capfd, monkeypatch):
Expand Down
Loading