Source code for roastery.term

"""
Utilities for logging and asking for user input.

Logging
-------

.. autofunction:: roastery.term.log
.. autofunction:: roastery.term.info
.. autofunction:: roastery.term.error
.. autofunction:: roastery.term.warn
.. autofunction:: roastery.term.hint
.. autofunction:: roastery.term.executing

User input
----------

.. autofunction:: roastery.term.ask
.. autofunction:: roastery.term.select_fuzzy_search
"""

import subprocess

from prompt_toolkit import prompt
from prompt_toolkit.enums import EditingMode
from prompt_toolkit.formatted_text import FormattedText
from prompt_toolkit.output.color_depth import ColorDepth
from rich import print as rprint
from rich.text import Text

__all__ = [
    "log",
    "info",
    "error",
    "warn",
    "hint",
    "executing",
    "ask",
    "select_fuzzy_search",
]


[docs] def error(*contents: str): """Log the `contents` to the terminal as an error. :param contents: String containing the log message in Rich Markup.""" log(*contents, header="error", style="red bold")
[docs] def warn(*contents: str): """Log the `contents` to the terminal as a warning. :param contents: String containing the log message in Rich Markup.""" log(*contents, header="warning", style="yellow bold")
[docs] def hint(*contents: str): """Log the `contents` to the terminal as a hint. :param contents: String containing the log message in Rich Markup.""" log(*contents, header="hint", style="blue bold")
[docs] def info(*contents: str): """Log the `contents` to the terminal in informational style. :param contents: String containing the log message in Rich Markup.""" # We don't prefix these messages with `INFO`. That's a little noisy. log(*contents, style="bold")
[docs] def executing(command: list[str]): """Log that the program is executing a command :param command: The command that the program is executing. This is a list, to allow easy integration with `subprocess.run()`. """ log(" ".join(command), header="executing", style="bold")
[docs] def log(*contents: str, style: str | None = None, header: str | None = None): """Log `contents` to the terminal in a given style. :param contents: String containing the log message in Rich Markup. :param style: Style to forward to `rich.Text` :param header: Header text to preface the log message with. """ if header: rprint(Text(f"| {header}", style=style)) for item in contents: rprint(Text.assemble(Text("| ", style=style), Text.from_markup(item))) print()
[docs] def ask(question: str, *, default: str = None) -> str: """Ask the user a `question`, returning their answer. Users will be able to enter their answer in a readline-style environment with vi-style keybindings. :param question: Question to prompt the user with. :param default: Default to pre-populate the readline env """ display = FormattedText( [ ( "bold blue", f"| {question} > ", ) ] ) # The color depth is so that `blue` refers to the color scheme in # use by the terminal. This means we respect the theme that was set # by the user. res = prompt( display, editing_mode=EditingMode.VI, default=default, color_depth=ColorDepth.ANSI_COLORS_ONLY, ) print() return res