Source code for echofilter.ui.style

"""
User interface styling, using ANSI codes and colorama.
"""

# This file is part of Echofilter.
#
# Copyright (C) 2020-2022  Scott C. Lowe and Offshore Energy Research Association (OERA)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

import contextlib

import colorama

colorama.init()


class _AbstractStyle(object):
    """
    Abstract class for formatting styles.
    """

    start = ""
    reset = ""

    @classmethod
    def apply(cls, string):
        """
        Apply the ANSI formatting.

        Parameters
        ----------
        string : str
            Input string to format.

        Returns
        -------
        formatted_string : str
            String prepended with a start ANSI code and appended with a
            reset ANSI code which undoes the start code.
        """
        return cls.start + string + cls.reset


[docs]class ErrorStyle(_AbstractStyle): """ Defines the style for an error string; red foreground. """ start = colorama.Fore.RED reset = colorama.Fore.RESET
[docs]class WarningStyle(_AbstractStyle): """ Defines the style for a warning string; cyan foreground. """ start = colorama.Fore.CYAN reset = colorama.Fore.RESET
[docs]class ProgressStyle(_AbstractStyle): """ Defines the style for a progress string; green foreground. """ start = colorama.Fore.GREEN reset = colorama.Fore.RESET
[docs]class DryrunStyle(_AbstractStyle): """ Defines the style for dry-run text; magenta foreground. """ start = colorama.Fore.MAGENTA reset = colorama.Fore.RESET
[docs]class SkipStyle(_AbstractStyle): """ Defines the style for skip text; yellow foreground. """ start = colorama.Fore.YELLOW reset = colorama.Fore.RESET
[docs]class OverwriteStyle(_AbstractStyle): """ Defines the style for overwrite text; bright blue. """ start = colorama.Fore.BLUE + colorama.Style.BRIGHT reset = colorama.Fore.RESET + colorama.Style.NORMAL
[docs]class HighlightStyle(_AbstractStyle): """ Defines the style for highlighted text; bright style. """ start = colorama.Style.BRIGHT reset = colorama.Style.NORMAL
[docs]class AsideStyle(_AbstractStyle): """ Defines the style for aside text; dim style. """ start = colorama.Style.DIM reset = colorama.Style.NORMAL
[docs]def error_fmt(string): """ Wrap a string in ANSI codes to render it in the style of an error. The style is applied when printed at the terminal. Parameters ---------- string : str Input string to format. Returns ------- formatted_string : str String prepended with a start ANSI code and appended with a reset ANSI code which undoes the start code. """ return ErrorStyle.apply(string)
[docs]def warning_fmt(string): """ Wrap a string in ANSI codes to render it in the style of a warning. The style is applied when printed at the terminal. Parameters ---------- string : str Input string to format. Returns ------- formatted_string : str String prepended with a start ANSI code and appended with a reset ANSI code which undoes the start code. """ return WarningStyle.apply(string)
[docs]def progress_fmt(string): """ Wrap a string in ANSI codes to render it in the style of progress text. The style is applied when printed at the terminal. Parameters ---------- string : str Input string to format. Returns ------- formatted_string : str String prepended with a start ANSI code and appended with a reset ANSI code which undoes the start code. """ return ProgressStyle.apply(string)
[docs]def dryrun_fmt(string): """ Wrap a string in ANSI codes to render it in the style of dry-run text. The style is applied when printed at the terminal. Parameters ---------- string : str Input string to format. Returns ------- formatted_string : str String prepended with a start ANSI code and appended with a reset ANSI code which undoes the start code. """ return DryrunStyle.apply(string)
[docs]def skip_fmt(string): """ Wrap a string in ANSI codes to render it in the style of a skip message. The style is applied when printed at the terminal. Parameters ---------- string : str Input string to format. Returns ------- formatted_string : str String prepended with a start ANSI code and appended with a reset ANSI code which undoes the start code. """ return SkipStyle.apply(string)
[docs]def overwrite_fmt(string): """ Wrap a string in ANSI codes to render it in the style of an overwrite. The style is applied when printed at the terminal. Parameters ---------- string : str Input string to format. Returns ------- formatted_string : str String prepended with a start ANSI code and appended with a reset ANSI code which undoes the start code. """ return OverwriteStyle.apply(string)
[docs]def highlight_fmt(string): """ Wrap a string in ANSI codes to render it in a highlighted style. The style is applied when printed at the terminal. Parameters ---------- string : str Input string to format. Returns ------- formatted_string : str String prepended with a start ANSI code and appended with a reset ANSI code which undoes the start code. """ return HighlightStyle.apply(string)
[docs]def aside_fmt(string): """ Wrap a string in ANSI codes to render it in an aside (de-emphasised) style. The style is applied when printed at the terminal. Parameters ---------- string : str Input string to format. Returns ------- formatted_string : str String prepended with a start ANSI code and appended with a reset ANSI code which undoes the start code. """ return AsideStyle.apply(string)
[docs]class error_message(contextlib.AbstractContextManager): """ Wrap an error message in ANSI codes to stylise its as red and bold. The style is applied when printed at the terminal. If the context is exited with an error, that error message will be red. Parameters ---------- message : str Text of the error message to stylise. Returns ------- str Stylised message. """ def __init__(self, message=""): # Make the error message be bold and red if message: # Bold for the message, then return to normal font weight message = HighlightStyle.start + message + HighlightStyle.reset # Make the error message, and everything which comes after it, be # red. We don't reset the colour in case we are inside a larger # error message, which should also be red. message = ErrorStyle.start + message self.message = message def __enter__(self): # Change all text sent to the terminal to be red, until we leave this # context print(ErrorStyle.start, end="") return self.message def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: # If we leave the context normally, reset the text color and # introduce a new line. # Now all changes we have made when we entered the context have # been reset. print(ErrorStyle.reset) else: # If we leave the context with an error, ensure the error message # is definitely red. print(ErrorStyle.start, end="")
[docs]class warning_message(contextlib.AbstractContextManager): """ Wrap a warning message in ANSI codes to stylise it as cyan and bold. The style is applied when printed at the terminal. All statements printed during the context will be in cyan. Parameters ---------- message : str Text of the warning message to stylise. Returns ------- str Stylised message. """ def __init__(self, message=""): # Make the error message be bold and cyan if message: # Bold for the message, then return to normal font weight message = HighlightStyle.start + message + HighlightStyle.reset # Make the warning message, and everything which comes after it, be # cyan. We don't reset the colour in case we are inside a larger # message, which should also be cyan. message = WarningStyle.start + message self.message = message def __enter__(self): # Change all text sent to the terminal to be cyan, until we leave this # context print(WarningStyle.start, end="") return self.message def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: # If we leave the context normally, reset the text color and # introduce a new line. # Now all changes we have made when we entered the context have # been reset. print(WarningStyle.reset) else: # If we leave the context with an error, use error message # styling instead. print(ErrorStyle.start, end="")