diff --git a/src/click/exceptions.py b/src/click/exceptions.py index 64e7f0ec51..0b8315166e 100644 --- a/src/click/exceptions.py +++ b/src/click/exceptions.py @@ -3,6 +3,7 @@ from gettext import ngettext from ._compat import get_text_stderr +from .globals import resolve_color_default from .utils import echo from .utils import format_filename @@ -29,6 +30,9 @@ class ClickException(Exception): def __init__(self, message: str) -> None: super().__init__(message) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color: t.Optional[bool] = resolve_color_default() self.message = message def format_message(self) -> str: @@ -41,7 +45,11 @@ def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: if file is None: file = get_text_stderr() - echo(_("Error: {message}").format(message=self.format_message()), file=file) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) class UsageError(ClickException): diff --git a/tests/test_testing.py b/tests/test_testing.py index 73010f6249..0d227f2a0a 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -5,6 +5,7 @@ import pytest import click +from click.exceptions import ClickException from click.testing import CliRunner @@ -199,6 +200,26 @@ def cli(): assert not result.exception +def test_with_color_errors(): + class CLIError(ClickException): + def format_message(self) -> str: + return click.style(self.message, fg="red") + + @click.command() + def cli(): + raise CLIError("Red error") + + runner = CliRunner() + + result = runner.invoke(cli) + assert result.output == "Error: Red error\n" + assert result.exception + + result = runner.invoke(cli, color=True) + assert result.output == f"Error: {click.style('Red error', fg='red')}\n" + assert result.exception + + def test_with_color_but_pause_not_blocking(): @click.command() def cli():