Module alexandria.shell

Shell utilities

Expand source code
# SPDX-FileCopyrightText: © 2021 Antonio López Rivera <antonlopezr99@gmail.com>
# SPDX-License-Identifier: GPL-3.0-only

"""
Shell utilities
---------------
"""


import os
import sys
from pyfiglet import Figlet
from datetime import datetime
from contextlib import contextmanager

from alexandria.data_structs.string import capletter
from alexandria.shell.color import colors, shell_supports_color
from alexandria.data_structs.string import join_set_distance


@contextmanager
def suppress_stdout():
    """
    Suppress console output.
    """
    with open(os.devnull, "w") as devnull:
        old_stdout = sys.stdout
        sys.stdout = devnull
        try:
            yield
        finally:
            sys.stdout = old_stdout


def str_color(string, color, color_bg="", highlight=""):
    """
    Generate a colored string using the available foreground and background
    colors and highlights, or ANSI codes directly.

    Automatically detects whether your shell supports ANSI color codes:
    in the case they're not, no ANSI codes will be added to your string.

    **
    Note:

    Incorrect ANSI codes will be rendered as text. Furthermore, not
    all ANSI codes may be supported by your shell of choice. In such
    cases the ANSI codes will be rendered as text as well.
    **

    ### Available colors:
    - 'black'     |   'brightBlack'
    - 'red'       |   'brightRed'
    - 'orange'    |   'brightOrange'        - might not be supported
    - 'green'     |   'brightGreen'
    - 'yellow'    |   'brightYellow'
    - 'blue'      |   'brightBlue'
    - 'magenta'   |   'brightMagenta'
    - 'cyan'      |   'brightCyan'
    - 'white'     |   'brightWhite'

    ### Available highlights:
    - bold
    - faint
    - italic
    - underline
    - crossed
    - framed
    - encircled
    - overlined

    :param string: String to print.
    :param color: Color.
    :param color_bg: Background color.
    :param highlight: Highlight.
    """
    if shell_supports_color():
        color_key       = "fg" + capletter(color, 0)
        color_bg_key    = "bg" + capletter(color_bg, 0)

        color           = colors.__dict__[color_key]    if color_key    in colors.__dict__.keys() else color
        color_bg        = colors.__dict__[color_bg_key] if color_bg_key in colors.__dict__.keys() else color_bg
        highlight       = colors.__dict__[highlight]    if highlight    in colors.__dict__.keys() else highlight

        bound = lambda s: colors.reset + s + colors.reset
        c_str = bound(color + color_bg + highlight + string)

        return c_str
    else:
        return string


def print_color(string, color, color_bg="", highlight="", **kwargs):
    """
    Print in color using the available foreground and background colors
    and highlights, or ANSI codes directly.

    Automatically detects whether your shell supports ANSI color codes:
    in the case they're not, the standard _print_ function is used.

    Supports keyword arguments for the standard _print_ function.

    **
    Note:

    Incorrect ANSI codes will be rendered as text. Furthermore, not
    all ANSI codes may be supported by your chosen shell. In such
    cases the ANSI codes will be rendered as text as well.
    **

    ### Available colors:
    - 'black'     |   'brightBlack'
    - 'red'       |   'brightRed'
    - 'orange'    |   'brightOrange'        - might not be supported
    - 'green'     |   'brightGreen'
    - 'yellow'    |   'brightYellow'
    - 'blue'      |   'brightBlue'
    - 'magenta'   |   'brightMagenta'
    - 'cyan'      |   'brightCyan'
    - 'white'     |   'brightWhite'

    ### Available highlights:
    - bold
    - faint
    - italic
    - underline
    - crossed
    - framed
    - encircled
    - overlined

    :param string: String to print.
    :param color: Color.
    :param color_bg: Background color.
    :param highlight: Highlight.
    :param kwargs: Keyword arguments for the standard _print_ function.
    """
    if shell_supports_color():
        c_str = str_color(string, color, color_bg, highlight)
        print(c_str, **kwargs)
    else:
        print(string, **kwargs)


def str_log(kind, msg,
            kind_color='black',
            kind_bg_color='white',
            msg_color='cyan',
            msg_bg_color="",
            t_color="",
            t_bg_color="",
            m=10,
            n=20,
            ):
    """
    Create rich logging string.

    TIME :: KIND :: MSG

    Colors rendered via the _str_color_ method. Refer to the Alexandria _str_color_
    and _colors_ class documentation for color input information.

    :param kind: Message kind. Akin to Python logging library's INFO, WARNING, CRITICAL.
                 As Alexandria does not pretend to replace logging, _kind_ is an arbitrary
                 convenient marker unaffected by _loglevel_ or any other output control
                 parameters.
    :param msg: Log message.
    :param kind_color: Kind text color.
    :param kind_bg_color: Kind background color.
    :param msg_color: Message text color.
    :param msg_bg_color: Message background color.
    :param t_color: Time text color.
    :param t_bg_color: Time background color.
    :param m: Separation between beginning of TIME string and beginning of KIND string.
    :param n: Separation between beginning of KIND string and beginning of MSG string.
    """
    # Current time
    t = datetime.now().strftime("%H:%M:%S")

    # Set distances
    r = join_set_distance(t + " ::", kind,  m)
    r = join_set_distance(r,         " ::", n)
    r = join_set_distance(r,         msg,   len(r))

    str_t, str_kind, str_msg = r.split(" :: ")

    # Take care not to color whitespace for KIND string
    #     It is assumed that all possible whitespace has
    #     been added by <<join_set_distance>> and is thus
    #     placed at the end of the string.
    n0 = len(str_kind)
    str_kind = str_kind.rstrip()
    n1 = len(str_kind)
    n_ws = n0 - n1

    # Color
    c_str_t    = str_color(str_t,    t_color,    t_bg_color)
    c_str_kind = str_color(str_kind, kind_color, kind_bg_color) + " "*n_ws
    c_str_msg  = str_color(str_msg,  msg_color,  msg_bg_color)

    # Join in final string
    r = " :: ".join([c_str_t, c_str_kind, c_str_msg])
    return r


def log(kind, msg,
        kind_color='black',
        kind_bg_color='white',
        msg_color='cyan',
        msg_bg_color="",
        t_color="",
        t_bg_color="",
        **kwargs):
    """
    Rich logging function.

    TIME :: KIND :: MSG

    Colors rendered via the _str_color_ method. Refer to the Alexandria _str_color_
    and _colors_ class documentation for color input information.

    :param kind: Message kind. Akin to Python logging library's INFO, WARNING, CRITICAL.
                 As Alexandria does not pretend to replace logging, _kind_ is an arbitrary
                 convenient marker unaffected by _loglevel_ or any other output control
                 parameters.
    :param msg: Log message.
    :param kind_color: Kind text color.
    :param kind_bg_color: Kind background color.
    :param msg_color: Message text color.
    :param msg_bg_color: Message background color.
    :param t_color: Time text color.
    :param t_bg_color: Time background color.
    :param kwargs: Keyword arguments for the standard _print_ function.
    """
    s = str_log(kind, msg,
                kind_color, kind_bg_color,
                msg_color, msg_bg_color,
                t_color, t_bg_color)
    print(s, **kwargs)


def title(text="Anchorage", font="big", color=""):
    """
    Generate Figlet title.

    :param text: Text to be rendered
    :param font: Figlet font to render the title
    :param color: Title color
    """
    f = Figlet(font=font)
    return str_color(f.renderText(text), color)


def print_numbered_list(lst, length=10):
    """
    Print numbered list.

    :param lst: List.
    :param length: Length of each of the strings containing a numeral and list entry.
    """
    for i in range(len(lst)):
        n = f"{i+1}"+"."
        s = join_set_distance(n, str(lst[i]), length)
        print(s)


def print_result(var, val, u, d=3, n=30):
    """
    Pretty print a calculated result.

    :param var: Variable
    :param val: Value
    :param u: Units
    :param d: Decimal digits
    :param n: Resulting string length
    :return:
    """
    s = join_set_distance(f'{var} = {val:,.{d}f}', u, n)
    print(s)


if __name__ == "__main__":
    log("INFO", "HELL NO" + str_color("MAAFUCKA", "red"))
    log("WARNING", "HELL NO" + str_color("MAAFUCKA", "red"))
    log("CRITICAL", "HELL NO" + str_color("MAAFUCKA", "red"))

Sub-modules

alexandria.shell.color

Shell color utilities

alexandria.shell.commands

Shell command utilities

Functions

def log(kind, msg, kind_color='black', kind_bg_color='white', msg_color='cyan', msg_bg_color='', t_color='', t_bg_color='', **kwargs)

Rich logging function.

TIME :: KIND :: MSG

Colors rendered via the str_color method. Refer to the Alexandria str_color and colors class documentation for color input information.

:param kind: Message kind. Akin to Python logging library's INFO, WARNING, CRITICAL. As Alexandria does not pretend to replace logging, kind is an arbitrary convenient marker unaffected by loglevel or any other output control parameters. :param msg: Log message. :param kind_color: Kind text color. :param kind_bg_color: Kind background color. :param msg_color: Message text color. :param msg_bg_color: Message background color. :param t_color: Time text color. :param t_bg_color: Time background color. :param kwargs: Keyword arguments for the standard print function.

Expand source code
def log(kind, msg,
        kind_color='black',
        kind_bg_color='white',
        msg_color='cyan',
        msg_bg_color="",
        t_color="",
        t_bg_color="",
        **kwargs):
    """
    Rich logging function.

    TIME :: KIND :: MSG

    Colors rendered via the _str_color_ method. Refer to the Alexandria _str_color_
    and _colors_ class documentation for color input information.

    :param kind: Message kind. Akin to Python logging library's INFO, WARNING, CRITICAL.
                 As Alexandria does not pretend to replace logging, _kind_ is an arbitrary
                 convenient marker unaffected by _loglevel_ or any other output control
                 parameters.
    :param msg: Log message.
    :param kind_color: Kind text color.
    :param kind_bg_color: Kind background color.
    :param msg_color: Message text color.
    :param msg_bg_color: Message background color.
    :param t_color: Time text color.
    :param t_bg_color: Time background color.
    :param kwargs: Keyword arguments for the standard _print_ function.
    """
    s = str_log(kind, msg,
                kind_color, kind_bg_color,
                msg_color, msg_bg_color,
                t_color, t_bg_color)
    print(s, **kwargs)
def print_color(string, color, color_bg='', highlight='', **kwargs)

Print in color using the available foreground and background colors and highlights, or ANSI codes directly.

Automatically detects whether your shell supports ANSI color codes: in the case they're not, the standard print function is used.

Supports keyword arguments for the standard print function.

** Note:

Incorrect ANSI codes will be rendered as text. Furthermore, not all ANSI codes may be supported by your chosen shell. In such cases the ANSI codes will be rendered as text as well. **

Available colors:

  • 'black' | 'brightBlack'
  • 'red' | 'brightRed'
  • 'orange' | 'brightOrange' - might not be supported
  • 'green' | 'brightGreen'
  • 'yellow' | 'brightYellow'
  • 'blue' | 'brightBlue'
  • 'magenta' | 'brightMagenta'
  • 'cyan' | 'brightCyan'
  • 'white' | 'brightWhite'

Available highlights:

  • bold
  • faint
  • italic
  • underline
  • crossed
  • framed
  • encircled
  • overlined

:param string: String to print. :param color: Color. :param color_bg: Background color. :param highlight: Highlight. :param kwargs: Keyword arguments for the standard print function.

Expand source code
def print_color(string, color, color_bg="", highlight="", **kwargs):
    """
    Print in color using the available foreground and background colors
    and highlights, or ANSI codes directly.

    Automatically detects whether your shell supports ANSI color codes:
    in the case they're not, the standard _print_ function is used.

    Supports keyword arguments for the standard _print_ function.

    **
    Note:

    Incorrect ANSI codes will be rendered as text. Furthermore, not
    all ANSI codes may be supported by your chosen shell. In such
    cases the ANSI codes will be rendered as text as well.
    **

    ### Available colors:
    - 'black'     |   'brightBlack'
    - 'red'       |   'brightRed'
    - 'orange'    |   'brightOrange'        - might not be supported
    - 'green'     |   'brightGreen'
    - 'yellow'    |   'brightYellow'
    - 'blue'      |   'brightBlue'
    - 'magenta'   |   'brightMagenta'
    - 'cyan'      |   'brightCyan'
    - 'white'     |   'brightWhite'

    ### Available highlights:
    - bold
    - faint
    - italic
    - underline
    - crossed
    - framed
    - encircled
    - overlined

    :param string: String to print.
    :param color: Color.
    :param color_bg: Background color.
    :param highlight: Highlight.
    :param kwargs: Keyword arguments for the standard _print_ function.
    """
    if shell_supports_color():
        c_str = str_color(string, color, color_bg, highlight)
        print(c_str, **kwargs)
    else:
        print(string, **kwargs)
def print_numbered_list(lst, length=10)

Print numbered list.

:param lst: List. :param length: Length of each of the strings containing a numeral and list entry.

Expand source code
def print_numbered_list(lst, length=10):
    """
    Print numbered list.

    :param lst: List.
    :param length: Length of each of the strings containing a numeral and list entry.
    """
    for i in range(len(lst)):
        n = f"{i+1}"+"."
        s = join_set_distance(n, str(lst[i]), length)
        print(s)
def print_result(var, val, u, d=3, n=30)

Pretty print a calculated result.

:param var: Variable :param val: Value :param u: Units :param d: Decimal digits :param n: Resulting string length :return:

Expand source code
def print_result(var, val, u, d=3, n=30):
    """
    Pretty print a calculated result.

    :param var: Variable
    :param val: Value
    :param u: Units
    :param d: Decimal digits
    :param n: Resulting string length
    :return:
    """
    s = join_set_distance(f'{var} = {val:,.{d}f}', u, n)
    print(s)
def str_color(string, color, color_bg='', highlight='')

Generate a colored string using the available foreground and background colors and highlights, or ANSI codes directly.

Automatically detects whether your shell supports ANSI color codes: in the case they're not, no ANSI codes will be added to your string.

** Note:

Incorrect ANSI codes will be rendered as text. Furthermore, not all ANSI codes may be supported by your shell of choice. In such cases the ANSI codes will be rendered as text as well. **

Available colors:

  • 'black' | 'brightBlack'
  • 'red' | 'brightRed'
  • 'orange' | 'brightOrange' - might not be supported
  • 'green' | 'brightGreen'
  • 'yellow' | 'brightYellow'
  • 'blue' | 'brightBlue'
  • 'magenta' | 'brightMagenta'
  • 'cyan' | 'brightCyan'
  • 'white' | 'brightWhite'

Available highlights:

  • bold
  • faint
  • italic
  • underline
  • crossed
  • framed
  • encircled
  • overlined

:param string: String to print. :param color: Color. :param color_bg: Background color. :param highlight: Highlight.

Expand source code
def str_color(string, color, color_bg="", highlight=""):
    """
    Generate a colored string using the available foreground and background
    colors and highlights, or ANSI codes directly.

    Automatically detects whether your shell supports ANSI color codes:
    in the case they're not, no ANSI codes will be added to your string.

    **
    Note:

    Incorrect ANSI codes will be rendered as text. Furthermore, not
    all ANSI codes may be supported by your shell of choice. In such
    cases the ANSI codes will be rendered as text as well.
    **

    ### Available colors:
    - 'black'     |   'brightBlack'
    - 'red'       |   'brightRed'
    - 'orange'    |   'brightOrange'        - might not be supported
    - 'green'     |   'brightGreen'
    - 'yellow'    |   'brightYellow'
    - 'blue'      |   'brightBlue'
    - 'magenta'   |   'brightMagenta'
    - 'cyan'      |   'brightCyan'
    - 'white'     |   'brightWhite'

    ### Available highlights:
    - bold
    - faint
    - italic
    - underline
    - crossed
    - framed
    - encircled
    - overlined

    :param string: String to print.
    :param color: Color.
    :param color_bg: Background color.
    :param highlight: Highlight.
    """
    if shell_supports_color():
        color_key       = "fg" + capletter(color, 0)
        color_bg_key    = "bg" + capletter(color_bg, 0)

        color           = colors.__dict__[color_key]    if color_key    in colors.__dict__.keys() else color
        color_bg        = colors.__dict__[color_bg_key] if color_bg_key in colors.__dict__.keys() else color_bg
        highlight       = colors.__dict__[highlight]    if highlight    in colors.__dict__.keys() else highlight

        bound = lambda s: colors.reset + s + colors.reset
        c_str = bound(color + color_bg + highlight + string)

        return c_str
    else:
        return string
def str_log(kind, msg, kind_color='black', kind_bg_color='white', msg_color='cyan', msg_bg_color='', t_color='', t_bg_color='', m=10, n=20)

Create rich logging string.

TIME :: KIND :: MSG

Colors rendered via the str_color method. Refer to the Alexandria str_color and colors class documentation for color input information.

:param kind: Message kind. Akin to Python logging library's INFO, WARNING, CRITICAL. As Alexandria does not pretend to replace logging, kind is an arbitrary convenient marker unaffected by loglevel or any other output control parameters. :param msg: Log message. :param kind_color: Kind text color. :param kind_bg_color: Kind background color. :param msg_color: Message text color. :param msg_bg_color: Message background color. :param t_color: Time text color. :param t_bg_color: Time background color. :param m: Separation between beginning of TIME string and beginning of KIND string. :param n: Separation between beginning of KIND string and beginning of MSG string.

Expand source code
def str_log(kind, msg,
            kind_color='black',
            kind_bg_color='white',
            msg_color='cyan',
            msg_bg_color="",
            t_color="",
            t_bg_color="",
            m=10,
            n=20,
            ):
    """
    Create rich logging string.

    TIME :: KIND :: MSG

    Colors rendered via the _str_color_ method. Refer to the Alexandria _str_color_
    and _colors_ class documentation for color input information.

    :param kind: Message kind. Akin to Python logging library's INFO, WARNING, CRITICAL.
                 As Alexandria does not pretend to replace logging, _kind_ is an arbitrary
                 convenient marker unaffected by _loglevel_ or any other output control
                 parameters.
    :param msg: Log message.
    :param kind_color: Kind text color.
    :param kind_bg_color: Kind background color.
    :param msg_color: Message text color.
    :param msg_bg_color: Message background color.
    :param t_color: Time text color.
    :param t_bg_color: Time background color.
    :param m: Separation between beginning of TIME string and beginning of KIND string.
    :param n: Separation between beginning of KIND string and beginning of MSG string.
    """
    # Current time
    t = datetime.now().strftime("%H:%M:%S")

    # Set distances
    r = join_set_distance(t + " ::", kind,  m)
    r = join_set_distance(r,         " ::", n)
    r = join_set_distance(r,         msg,   len(r))

    str_t, str_kind, str_msg = r.split(" :: ")

    # Take care not to color whitespace for KIND string
    #     It is assumed that all possible whitespace has
    #     been added by <<join_set_distance>> and is thus
    #     placed at the end of the string.
    n0 = len(str_kind)
    str_kind = str_kind.rstrip()
    n1 = len(str_kind)
    n_ws = n0 - n1

    # Color
    c_str_t    = str_color(str_t,    t_color,    t_bg_color)
    c_str_kind = str_color(str_kind, kind_color, kind_bg_color) + " "*n_ws
    c_str_msg  = str_color(str_msg,  msg_color,  msg_bg_color)

    # Join in final string
    r = " :: ".join([c_str_t, c_str_kind, c_str_msg])
    return r
def suppress_stdout()

Suppress console output.

Expand source code
@contextmanager
def suppress_stdout():
    """
    Suppress console output.
    """
    with open(os.devnull, "w") as devnull:
        old_stdout = sys.stdout
        sys.stdout = devnull
        try:
            yield
        finally:
            sys.stdout = old_stdout
def title(text='Anchorage', font='big', color='')

Generate Figlet title.

:param text: Text to be rendered :param font: Figlet font to render the title :param color: Title color

Expand source code
def title(text="Anchorage", font="big", color=""):
    """
    Generate Figlet title.

    :param text: Text to be rendered
    :param font: Figlet font to render the title
    :param color: Title color
    """
    f = Figlet(font=font)
    return str_color(f.renderText(text), color)