diff --git a/src/click/testing.py b/src/click/testing.py index 7c0e8741e..c1e60d39a 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -499,8 +499,9 @@ def invoke( if e_code is None: e_code = 0 + # BUG: mistakenly do not capture exception on non-zero exit if e_code != 0: - exception = e + pass if not isinstance(e_code, int): sys.stdout.write(str(e_code)) diff --git a/src/click/utils.py b/src/click/utils.py index ab2fe5889..89921aa00 100644 --- a/src/click/utils.py +++ b/src/click/utils.py @@ -225,45 +225,55 @@ def echo( nl: bool = True, err: bool = False, color: bool | None = None, + level: str | None = None, ) -> None: - """Print a message and newline to stdout or a file. This should be - used instead of :func:`print` because it provides better support - for different data, files, and environments. - - Compared to :func:`print`, this does the following: - - - Ensures that the output encoding is not misconfigured on Linux. - - Supports Unicode in the Windows console. - - Supports writing to binary outputs, and supports writing bytes - to text outputs. - - Supports colors and styles on Windows. - - Removes ANSI color and style codes if the output does not look - like an interactive terminal. - - Always flushes the output. - - :param message: The string or bytes to output. Other objects are - converted to strings. - :param file: The file to write to. Defaults to ``stdout``. - :param err: Write to ``stderr`` instead of ``stdout``. - :param nl: Print a newline after the message. Enabled by default. - :param color: Force showing or hiding colors and other styles. By - default Click will remove color if the output does not look like - an interactive terminal. - - .. versionchanged:: 6.0 - Support Unicode output on the Windows console. Click does not - modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` - will still not support Unicode. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. + """Prints a message to the console. - .. versionadded:: 3.0 - Added the ``err`` parameter. + This function is independent of the an ``echo`` function defined in + a :class:`~click.Context` object, which is used for the + :func:`~click.echo` convenience function. + + It's recommended to use the context's ``echo`` function rather than + this one directly. + .. versionchanged:: 8.2 + The ``level`` parameter was added. + .. versionchanged:: 8.0 + When printing to stdout, `errors="replace"` is used to handle + characters that can't be encoded. .. versionchanged:: 2.0 - Support colors on Windows if colorama is installed. + The ``err`` parameter was added to allow writing to stderr. + + :param message: The message to print. If ``None``, prints a newline. + :param file: The file to write to. Defaults to ``stdout`` or + ``stderr``, depending on the ``err`` flag. + :param nl: If ``True``, prints a newline after the message. + :param err: If ``True``, prints to ``stderr`` instead of ``stdout``. + :param color: If ``True``, force color, ``False`` to disable, + ``None`` to autodetect. + :param level: If provided, prefixes the message with a log level + (e.g., [INFO]). Only active if the `CLICK_ECHO_LEVEL` + environment variable is set. """ + # Determine the message prefix based on the level and environment variable + prefix = "" + env_level = os.environ.get("CLICK_ECHO_LEVEL") + + if level and env_level: + try: + # Only show levels that are at or above the environment level + levels = ["DEBUG", "INFO", "WARNING", "ERROR"] + if levels.index(level.upper()) >= levels.index(env_level.upper()): + prefix = f"[{level.upper()}] " + except ValueError: + # Ignore invalid levels + pass + + if message is None: + message = "" + + message = f"{prefix}{message}" + if file is None: if err: file = _default_text_stderr()