## @file
# Copyright (c) 2023, The OCE Build Authors. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
##
"""CLI Logging Utilities."""
#TODO: Refactor to use the native logging hook
# @see https://rich.readthedocs.io/en/stable/logging.html
import inspect
from functools import partial
from typing import List, Optional
from ocebuild.parsers.dict import nested_get
#NOTE: This import was remapped from 'ocebuild_cli' to 'ocebuild.cli'.
import ocebuild.cli._lib as lib
import ocebuild.cli.console as Console
from ocebuild.cli.console import traceback_wrapper, LOGGING_THEME
def _format_url(url: str) -> str:
"""Formats a URL for the CLI."""
return f'[bold][link={url}]{url}[/link][/bold]'
def _format_label(msg: str,
label: str,
color: Optional[str]=None,
hint: Optional[str]=None,
) -> str:
"""Formats a multi-line labeled message for the CLI."""
color = nested_get(LOGGING_THEME, [f"logging.level.{label.lower()}"],
default=color)
padding = " " * (8 - len(label))
fmt_msg = f"[{color}][bold]{label}[/bold]: {padding}[/{color}]{msg}"
if hint:
indent = ' ' * (len(label) + 2)
fmt_msg += f"\n{indent}{padding}{hint}"
return fmt_msg
[docs]def echo(msg: str='', *args, log: bool=True, **kwargs) -> None:
"""Stylized echo for the CLI.
Args:
msg: The message to print.
*args: Additional arguments to pass to `console.print()`.
**kwargs: Additional keyword arguments to pass to `console.print()`.
Example:
>>> echo('This is a message.')
# -> This is a message.
"""
if log:
fn = partial(Console.CONSOLE.log, _stack_offset=3)
else:
fn = Console.CONSOLE.print
fn(msg, *args, markup=True, **kwargs)
################################################################################
# CLI Logging Utilities #
################################################################################
[docs]def debug(msg: str, *args, **kwargs):
"""Prints a debug message.
This function is a wrapper for `echo()` that only prints if the global
`DEBUG` flag is set.
"""
if lib.DEBUG:
echo(_format_label(f"[dim]{msg}[/dim]", 'DEBUG'), *args, log=True, **kwargs)
[docs]def info(msg: str, *args, **kwargs):
"""Prints an info message.
This function is a wrapper for `echo()` that only prints if the global
`VERBOSE` flag is set.
"""
if lib.VERBOSE:
echo(_format_label(msg, 'INFO'), *args, log=True, **kwargs)
[docs]def success(msg: str, *args, **kwargs):
"""Prints a success message."""
echo(_format_label(msg, 'SUCCESS'), *args, log=True, **kwargs)
[docs]def error(msg: str,
hint: Optional[str]=None,
label: str='ERROR',
traceback: bool=False,
suppress: Optional[List[str]]=None,
hide_locals: bool=False,
**kwargs
) -> None:
"""Stylized error message for the CLI.
Args:
msg: The error message to print.
label: The label to print before the error message.
hint: A hint to print after the error message. (Optional)
traceback: Whether to print a traceback. (Optional)
suppress: A list of filepaths to suppress from the traceback. (Optional)
Example:
>>> error('This is an error message.')
# -> Error: This is an error message.
"""
color = nested_get(LOGGING_THEME, [f"logging.level.error"])
echo(_format_label(msg, label, color, hint), log=True, **kwargs)
# Wrap the public traceback frames if specified
if traceback:
traceback_wrapper(suppress=suppress, hide_locals=hide_locals)
[docs]def abort(msg: str,
hint: Optional[str]=None,
traceback: bool=True
) -> None:
"""Stylized abort message for the CLI.
This function is a wrapper for `error()` that exits with a non-zero exit code.
By default, a full traceback is printed using `wrap_exception()`, hiding
internal stack frames.
Args:
msg: The abort message to print.
hint: A hint to print after the abort message. (Optional)
traceback: Whether to print a traceback; enabled by default. (Optional)
Example:
>>> abort('This is an abort message.')
# -> Abort: This is an abort message.
# (rich.console `print_exception()` traceback)
"""
caller = inspect.stack()[1].filename
error(msg, hint, 'ABORT', traceback, suppress=[caller],
hide_locals=True,
_stack_offset=4)
__all__ = [
# Functions (6)
"echo",
"debug",
"info",
"success",
"error",
"abort"
]