Module meerschaum.utils.formatting

Utilities for formatting output text

Expand source code
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8

"""
Utilities for formatting output text
"""

from __future__ import annotations
import platform
import os
import sys
from meerschaum.utils.typing import Optional, Union, Any
from meerschaum.utils.formatting._shell import make_header
from meerschaum.utils.formatting._pprint import pprint
from meerschaum.utils.formatting._pipes import pprint_pipes, highlight_pipes
from meerschaum.utils.threading import Lock, RLock

_attrs = {
    'ANSI': None,
    'UNICODE': None,
    'CHARSET': None,
}
__all__ = sorted([
    'ANSI', 'CHARSET', 'UNICODE',
    'colored',
    'translate_rich_to_termcolor',
    'get_console',
    'print_tuple',
    'fill_ansi',
    'pprint',
    'highlight_pipes',
    'pprint_pipes',
    'make_header',
])
__pdoc__ = {}
_locks = {
    '_colorama_init': RLock(),
}


def colored_fallback(*args, **kw):
    return ' '.join(args)

def translate_rich_to_termcolor(*colors) -> tuple:
    """Translate between rich and more_termcolor terminology.
    This is probably prone to breaking.

    Parameters
    ----------
    *colors :
        

    Returns
    -------

    """
    _colors = []
    for c in colors:
        _c_list = []
        ### handle 'bright'
        c = c.replace('bright_', 'bright ')

        ### handle 'on'
        if ' on ' in c:
            _on = c.split(' on ')
            _colors.append(_on[0])
            for _c in _on[1:]:
                _c_list.append('on ' + _c)
        else:
            _c_list += [c]

        _colors += _c_list

    return tuple(_colors)


def rich_text_to_str(text: 'rich.text.Text') -> str:
    """Convert a `rich.text.Text` object to a string with ANSI in-tact."""
    _console = get_console()
    if _console is None:
        return str(text)
    with console.capture() as cap:
        console.print(text)
    string = cap.get()
    return string[:-1]


def _init():
    """
    Initial color settings (mostly for Windows).
    """
    if platform.system() != "Windows":
        return
    if 'PYTHONIOENCODING' not in os.environ:
        os.environ['PYTHONIOENCODING'] = 'utf-8'
    if 'PYTHONLEGACYWINDOWSSTDIO' not in os.environ:
        os.environ['PYTHONLEGACYWINDOWSSTDIO'] = 'utf-8'
    sys.stdin.reconfigure(encoding='utf-8')
    sys.stdout.reconfigure(encoding='utf-8')
    sys.stderr.reconfigure(encoding='utf-8')

    from ctypes import windll
    k = windll.kernel32
    k.SetConsoleMode(k.GetStdHandle(-11), 7)
    os.system("color")

    from meerschaum.utils.packages import attempt_import
    ### init colorama for Windows color output
    colorama, more_termcolor = attempt_import(
        'colorama',
        'more_termcolor',
        lazy = False,
        warn = False,
        color = False,
    )
    try:
        colorama.init(autoreset=False)
        success = True
    except Exception as e:
        import traceback
        traceback.print_exc()
        _attrs['ANSI'], _attrs['UNICODE'], _attrs['CHARSET'] = False, False, 'ascii'
        success = False

    if more_termcolor is None:
        _attrs['ANSI'], _attrs['UNICODE'], _attrs['CHARSET'] = False, False, 'ascii'
        success = False

    return success

_colorama_init = False
def colored(text: str, *colors, as_rich_text: bool=False, **kw) -> Union[str, 'rich.text.Text']:
    """Apply colors and rich styles to a string.
    If a `style` keyword is provided, a `rich.text.Text` object will be parsed into a string.
    Otherwise attempt to use the legacy `more_termcolor.colored` method.

    Parameters
    ----------
    text: str
        The string to apply formatting to.
        
    *colors:
        A list of colors to pass to `more_termcolor.colored()`.
        Has no effect if `style` is provided.

    style: str, default None
        If provided, pass to `rich` for processing.

    as_rich_text: bool, default False
        If `True`, return a `rich.Text` object.
        `style` must be provided.
        
    **kw:
        Keyword arguments to pass to `rich.text.Text` or `more_termcolor`.
        

    Returns
    -------
    An ANSI-formatted string or a `rich.text.Text` object if `as_rich_text` is `True`.

    """
    from meerschaum.utils.packages import import_rich, attempt_import
    global _colorama_init
    _init()
    with _locks['_colorama_init']:
        if not _colorama_init:
            _colorama_init = _init()

    if 'style' in kw:
        rich = import_rich()
        rich_text = attempt_import('rich.text')
        text_obj = rich_text.Text(text, **kw)
        if as_rich_text:
            return text_obj
        return rich_text_to_str(text_obj)

    more_termcolor = attempt_import('more_termcolor', lazy=False)
    try:
        colored_text = more_termcolor.colored(text, *colors, **kw)
    except Exception as e:
        colored_text = None

    if colored_text is not None:
        return colored_text

    try:
        _colors = translate_rich_to_termcolor(*colors)
        colored_text = more_termcolor.colored(text, *_colors, **kw)
    except Exception as e:
        colored_text = None

    if colored_text is None:
        ### NOTE: warn here?
        return text

    return colored_text

console = None
def get_console():
    """Get the rich console."""
    global console
    from meerschaum.utils.packages import import_rich, attempt_import
    rich = import_rich()
    rich_console = attempt_import('rich.console')
    try:
        console = rich_console.Console(force_terminal=True, color_system='truecolor')
    except Exception as e:
        console = None
    return console


def print_tuple(
        tup: tuple,
        skip_common: bool = True,
        common_only: bool = False,
        upper_padding: int = 0,
        lower_padding: int = 0,
        _progress: Optional['rich.progress.Progress'] = None,
    ) -> None:
    """Print `meerschaum.utils.typing.SuccessTuple`."""
    from meerschaum.config.static import STATIC_CONFIG
    _init()
    try:
        status = 'success' if tup[0] else 'failure'
    except TypeError:
        status = 'failure'
        tup = None, None

    omit_messages = STATIC_CONFIG['system']['success']['ignore']

    do_print = True

    if common_only:
        skip_common = False
        do_print = tup[1] in omit_messages

    if skip_common:
        do_print = tup[1] not in omit_messages

    if do_print:
        ANSI, CHARSET = __getattr__('ANSI'), __getattr__('CHARSET')
        from meerschaum.config import get_config
        status_config = get_config('formatting', status, patch=True)

        msg = ' ' + status_config[CHARSET]['icon'] + ' ' + str(tup[1])
        lines = msg.split('\n')
        lines = [lines[0]] + [
            (('    ' + line if not line.startswith(' ') else line))
            for line in lines[1:]
        ]
        if ANSI:
            lines[0] = fill_ansi(highlight_pipes(lines[0]), **status_config['ansi']['rich'])
        msg = '\n'.join(lines)
        msg = ('\n' * upper_padding) + msg + ('\n' * lower_padding)
        print(msg)


def fill_ansi(string: str, style: str = '') -> str:
    """
    Fill in non-formatted segments of ANSI text.

    Parameters
    ----------
    string: str
        A string which contains ANSI escape codes.

    style: str
        Style arguments to pass to `rich.text.Text`.

    Returns
    -------
    A string with ANSI styling applied to the segments which don't yet have a style applied.
    """
    from meerschaum.utils.packages import import_rich, attempt_import
    from meerschaum.utils.misc import iterate_chunks
    rich = import_rich()
    Text = attempt_import('rich.text').Text
    try:
        msg = Text.from_ansi(string)
    except AttributeError as e:
        import traceback
        traceback.print_stack()
        msg = ''
    plain_indices = []
    for left_span, right_span in iterate_chunks(msg.spans, 2, fillvalue=len(msg)):
        left = left_span.end
        right = right_span.start if not isinstance(right_span, int) else right_span
        if left != right:
            plain_indices.append((left, right))
    if msg.spans:
        if msg.spans[0].start != 0:
            plain_indices = [(0, msg.spans[0].start)] + plain_indices
        if plain_indices and msg.spans[-1].end != len(msg) and plain_indices[-1][1] != len(msg):
            plain_indices.append((msg.spans[-1].end, len(msg)))

    if plain_indices:
        for left, right in plain_indices:
            msg.stylize(style, left, right)
    else:
        msg = Text(str(msg), style)

    return rich_text_to_str(msg)

def __getattr__(name: str) -> str:
    """
    Lazily load module-level variables.
    """
    if name.startswith('__') and name.endswith('__'):
        raise AttributeError("Cannot import dunders from this module.")

    if name in _attrs:
        if _attrs[name] is not None:
            return _attrs[name]
        from meerschaum.config import get_config
        if name.lower() in get_config('formatting'):
            _attrs[name] = get_config('formatting', name.lower())
        elif name == 'CHARSET':
            _attrs[name] = 'unicode' if __getattr__('UNICODE') else 'ascii'
        return _attrs[name]
    
    if name == '__wrapped__':
        import sys
        return sys.modules[__name__]
    if name == '__all__':
        return __all__

    try:
        return globals()[name]
    except KeyError:
        raise AttributeError(f"Could not find '{name}'")

Functions

def colored(text: str, *colors, as_rich_text: bool = False, **kw) ‑> Union[str, 'rich.text.Text']

Apply colors and rich styles to a string. If a style keyword is provided, a rich.text.Text object will be parsed into a string. Otherwise attempt to use the legacy more_termcolor.colored method.

Parameters

text : str
The string to apply formatting to.

*colors: A list of colors to pass to more_termcolor.colored(). Has no effect if style is provided.

style : str, default None
If provided, pass to rich for processing.
as_rich_text : bool, default False
If True, return a rich.Text object. style must be provided.

**kw: Keyword arguments to pass to rich.text.Text or more_termcolor.

Returns

An ANSI-formatted string or a rich.text.Text object if as_rich_text is True.

Expand source code
def colored(text: str, *colors, as_rich_text: bool=False, **kw) -> Union[str, 'rich.text.Text']:
    """Apply colors and rich styles to a string.
    If a `style` keyword is provided, a `rich.text.Text` object will be parsed into a string.
    Otherwise attempt to use the legacy `more_termcolor.colored` method.

    Parameters
    ----------
    text: str
        The string to apply formatting to.
        
    *colors:
        A list of colors to pass to `more_termcolor.colored()`.
        Has no effect if `style` is provided.

    style: str, default None
        If provided, pass to `rich` for processing.

    as_rich_text: bool, default False
        If `True`, return a `rich.Text` object.
        `style` must be provided.
        
    **kw:
        Keyword arguments to pass to `rich.text.Text` or `more_termcolor`.
        

    Returns
    -------
    An ANSI-formatted string or a `rich.text.Text` object if `as_rich_text` is `True`.

    """
    from meerschaum.utils.packages import import_rich, attempt_import
    global _colorama_init
    _init()
    with _locks['_colorama_init']:
        if not _colorama_init:
            _colorama_init = _init()

    if 'style' in kw:
        rich = import_rich()
        rich_text = attempt_import('rich.text')
        text_obj = rich_text.Text(text, **kw)
        if as_rich_text:
            return text_obj
        return rich_text_to_str(text_obj)

    more_termcolor = attempt_import('more_termcolor', lazy=False)
    try:
        colored_text = more_termcolor.colored(text, *colors, **kw)
    except Exception as e:
        colored_text = None

    if colored_text is not None:
        return colored_text

    try:
        _colors = translate_rich_to_termcolor(*colors)
        colored_text = more_termcolor.colored(text, *_colors, **kw)
    except Exception as e:
        colored_text = None

    if colored_text is None:
        ### NOTE: warn here?
        return text

    return colored_text
def fill_ansi(string: str, style: str = '') ‑> str

Fill in non-formatted segments of ANSI text.

Parameters

string : str
A string which contains ANSI escape codes.
style : str
Style arguments to pass to rich.text.Text.

Returns

A string with ANSI styling applied to the segments which don't yet have a style applied.

Expand source code
def fill_ansi(string: str, style: str = '') -> str:
    """
    Fill in non-formatted segments of ANSI text.

    Parameters
    ----------
    string: str
        A string which contains ANSI escape codes.

    style: str
        Style arguments to pass to `rich.text.Text`.

    Returns
    -------
    A string with ANSI styling applied to the segments which don't yet have a style applied.
    """
    from meerschaum.utils.packages import import_rich, attempt_import
    from meerschaum.utils.misc import iterate_chunks
    rich = import_rich()
    Text = attempt_import('rich.text').Text
    try:
        msg = Text.from_ansi(string)
    except AttributeError as e:
        import traceback
        traceback.print_stack()
        msg = ''
    plain_indices = []
    for left_span, right_span in iterate_chunks(msg.spans, 2, fillvalue=len(msg)):
        left = left_span.end
        right = right_span.start if not isinstance(right_span, int) else right_span
        if left != right:
            plain_indices.append((left, right))
    if msg.spans:
        if msg.spans[0].start != 0:
            plain_indices = [(0, msg.spans[0].start)] + plain_indices
        if plain_indices and msg.spans[-1].end != len(msg) and plain_indices[-1][1] != len(msg):
            plain_indices.append((msg.spans[-1].end, len(msg)))

    if plain_indices:
        for left, right in plain_indices:
            msg.stylize(style, left, right)
    else:
        msg = Text(str(msg), style)

    return rich_text_to_str(msg)
def get_console()

Get the rich console.

Expand source code
def get_console():
    """Get the rich console."""
    global console
    from meerschaum.utils.packages import import_rich, attempt_import
    rich = import_rich()
    rich_console = attempt_import('rich.console')
    try:
        console = rich_console.Console(force_terminal=True, color_system='truecolor')
    except Exception as e:
        console = None
    return console
def highlight_pipes(message: str) ‑> str

Add syntax highlighting to an info message containing stringified Pipe objects.

Expand source code
def highlight_pipes(message: str) -> str:
    """
    Add syntax highlighting to an info message containing stringified `meerschaum.Pipe` objects.
    """
    if 'Pipe(' not in message or ')' not in message:
        return message
    from meerschaum import Pipe
    segments = message.split('Pipe(')
    msg = ''
    _d = {}
    for i, segment in enumerate(segments):
        if ',' in segment and ')' in segment:
            paren_index = segment.find(')') + 1
            code = "_d['pipe'] = Pipe(" + segment[:paren_index]
            try:
                exec(code)
                _to_add = pipe_repr(_d['pipe']) + segment[paren_index:]
            except Exception as e:
                _to_add = 'Pipe(' + segment
            msg += _to_add
            continue
        msg += segment
    return msg
def make_header(message: str, ruler: str = '─') ‑> str

Format a message string with a ruler. Length of the ruler is the length of the longest word.

Example:
    'My

header' -> 'My header ──────'

Expand source code
def make_header(
        message : str,
        ruler : str = '─',
    ) -> str:
    """Format a message string with a ruler.
    Length of the ruler is the length of the longest word.
    
    Example:
        'My\nheader' -> 'My\nheader\n──────'
    """

    from meerschaum.utils.formatting import ANSI, UNICODE, colored
    if not UNICODE:
        ruler = '-'
    words = message.split('\n')
    max_length = 0
    for w in words:
        length = len(w)
        if length > max_length:
            max_length = length

    s = message + "\n"
    for i in range(max_length):
        s += ruler
    return s
def pprint(*args, detect_password: bool = True, nopretty: bool = False, **kw)

Pretty print an object according to the configured ANSI and UNICODE settings. If detect_password is True (default), search and replace passwords with '*' characters. Does not mutate objects.

Parameters

*args :

detect_password : bool :
(Default value = True)
nopretty : bool :
(Default value = False)

**kw :

Returns

Expand source code
def pprint(
        *args,
        detect_password : bool = True,
        nopretty : bool = False,
        **kw
    ):
    """Pretty print an object according to the configured ANSI and UNICODE settings.
    If detect_password is True (default), search and replace passwords with '*' characters.
    Does not mutate objects.

    Parameters
    ----------
    *args :
        
    detect_password : bool :
         (Default value = True)
    nopretty : bool :
         (Default value = False)
    **kw :
        

    Returns
    -------

    """
    from meerschaum.utils.packages import attempt_import, import_rich
    from meerschaum.utils.formatting import ANSI, UNICODE, get_console
    from meerschaum.utils.warnings import error
    from meerschaum.utils.misc import replace_password, dict_from_od, filter_keywords
    from collections import OrderedDict
    import copy, json
    modify = True
    rich_pprint = None
    if ANSI and not nopretty:
        rich = import_rich()
        if rich is not None:
            rich_pretty = attempt_import('rich.pretty')
        if rich_pretty is not None:
            def _rich_pprint(*args, **kw):
                _console = get_console()
                _kw = filter_keywords(_console.print, **kw)
                _console.print(*args, **_kw)
            rich_pprint = _rich_pprint
    elif not nopretty:
        pprintpp = attempt_import('pprintpp', warn=False)
        try:
            _pprint = pprintpp.pprint
        except Exception as e:
            import pprint as _pprint_module
            _pprint = _pprint_module.pprint

    func = (
        _pprint if rich_pprint is None else rich_pprint
    ) if not nopretty else print

    try:
        args_copy = copy.deepcopy(args)
    except Exception as e:
        args_copy = args
        modify = False
    _args = []
    for a in args:
        c = a
        ### convert OrderedDict into dict
        if isinstance(a, OrderedDict) or issubclass(type(a), OrderedDict):
            c = dict_from_od(copy.deepcopy(c))
        _args.append(c)
    args = _args

    _args = list(args)
    if detect_password and modify:
        _args = []
        for a in args:
            c = a
            if isinstance(c, dict):
                c = replace_password(copy.deepcopy(c))
            if nopretty:
                try:
                    c = json.dumps(c)
                    is_json = True
                except Exception as e:
                    is_json = False
                if not is_json:
                    try:
                        c = str(c)
                    except Exception as e:
                        pass
            _args.append(c)

    ### filter out unsupported keywords
    func_kw = filter_keywords(func, **kw) if not nopretty else {}
    error_msg = None
    try:
        func(*_args, **func_kw)
    except Exception as e:
        error_msg = e
    if error_msg is not None:
        error(error_msg)
def pprint_pipes(pipes: PipesDict) ‑> None

Print a stylized tree of a Pipes dictionary. Supports ANSI and UNICODE global settings.

Expand source code
def pprint_pipes(pipes: PipesDict) -> None:
    """Print a stylized tree of a Pipes dictionary.
    Supports ANSI and UNICODE global settings."""
    from meerschaum.utils.warnings import error
    from meerschaum.utils.packages import attempt_import, import_rich
    from meerschaum.utils.misc import sorted_dict, replace_pipes_in_dict
    from meerschaum.utils.formatting import UNICODE, ANSI, CHARSET, pprint, colored, get_console
    from meerschaum.config import get_config
    import copy
    rich = import_rich('rich', warn=False)
    Text = None
    if rich is not None:
        rich_text = attempt_import('rich.text', lazy=False)
        Text = rich_text.Text

    icons = get_config('formatting', 'pipes', CHARSET, 'icons')
    styles = get_config('formatting', 'pipes', 'ansi', 'styles')
    if not ANSI:
        styles = {k: '' for k in styles}
    print()

    def ascii_print_pipes():
        """Print the dictionary with no unicode allowed. Also works in case rich fails to import
        (though rich should auto-install when `attempt_import()` is called)."""
        asciitree = attempt_import('asciitree')
        ascii_dict, replace_dict = {}, {'connector': {}, 'metric': {}, 'location': {}}
        for conn_keys, metrics in pipes.items():
            _colored_conn_key = colored(icons['connector'] + conn_keys, style=styles['connector'])
            if Text is not None:
                replace_dict['connector'][_colored_conn_key] = (
                    Text(conn_keys, style=styles['connector'])
                )
            ascii_dict[_colored_conn_key] = {}
            for metric, locations in metrics.items():
                _colored_metric_key = colored(icons['metric'] + metric, style=styles['metric'])
                if Text is not None:
                    replace_dict['metric'][_colored_metric_key] = (
                        Text(metric, style=styles['metric'])
                    )
                ascii_dict[_colored_conn_key][_colored_metric_key] = {}
                for location, pipe in locations.items():
                    _location_style = styles[('none' if location is None else 'location')]
                    pipe_addendum = '\n         ' + pipe.__repr__() + '\n'
                    _colored_location = colored(
                        icons['location'] + str(location), style=_location_style
                    )
                    _colored_location_key = _colored_location + pipe_addendum
                    if Text is not None:
                        replace_dict['location'][_colored_location] = (
                            Text(str(location), style=_location_style)
                        )
                    ascii_dict[_colored_conn_key][_colored_metric_key][_colored_location_key] = {}

        tree = asciitree.LeftAligned()
        output = ''
        cols = []

        ### This is pretty terrible, unreadable code.
        ### Please know that I'm normally better than this.
        key_str = (
            (Text("     ") if Text is not None else "     ") +
            (
                Text("Key", style='underline') if Text is not None else
                colored("Key", style='underline')
            ) + (Text('\n\n  ') if Text is not None else '\n\n  ') +
            (
                Text("Connector", style=styles['connector']) if Text is not None else
                colored("Connector", style=styles['connector'])
            ) + (Text('\n   +-- ') if Text is not None else '\n   +-- ') +
            (
                Text("Metric", style=styles['metric']) if Text is not None else
                colored("Metric", style=styles['metric'])
            ) + (Text('\n       +-- ') if Text is not None else '\n       +-- ') +
            (
                Text("Location", style=styles['location']) if Text is not None else
                colored("Location", style=styles['location'])
            ) + (Text('\n\n') if Text is not None else '\n\n')
        )

        output += str(key_str)
        cols.append(key_str)

        def replace_tree_text(tree_str : str) -> Text:
            """Replace the colored words with stylized Text instead.
            Is not executed if ANSI and UNICODE are disabled."""
            tree_text = Text(tree_str) if Text is not None else None
            for k, v in replace_dict.items():
                for _colored, _text in v.items():
                    parts = []
                    lines = tree_text.split(_colored)
                    for part in lines:
                        parts += [part, _text]
                    if lines[-1] != Text(''):
                        parts = parts[:-1]
                    _tree_text = Text('')
                    for part in parts:
                        _tree_text += part
                    tree_text = _tree_text
            return tree_text

        tree_output = ""
        for k, v in ascii_dict.items():
            branch = {k : v}
            tree_output += tree(branch) + '\n\n'
            if not UNICODE and not ANSI:
                _col = (Text(tree(branch)) if Text is not None else tree(branch))
            else:
                _col = replace_tree_text(tree(branch))
            cols.append(_col)
        if len(output) > 0:
            tree_output = tree_output[:-2]
        output += tree_output

        if rich is None:
            return print(output)

        rich_columns = attempt_import('rich.columns')
        Columns = rich_columns.Columns
        columns = Columns(cols)
        get_console().print(columns)

    if not UNICODE:
        return ascii_print_pipes()

    rich_panel, rich_tree, rich_text, rich_columns, rich_table = attempt_import(
        'rich.panel',
        'rich.tree',
        'rich.text',
        'rich.columns',
        'rich.table',
    )
    from rich import box
    Panel = rich_panel.Panel
    Tree = rich_tree.Tree
    Text = rich_text.Text
    Columns = rich_columns.Columns
    Table = rich_table.Table

    key_panel = Panel(
        (
            Text("\n") +
            Text(icons['connector'] + "Connector", style=styles['connector']) + Text("\n\n") +
            Text(icons['metric'] + "Metric", style=styles['metric']) + Text("\n\n") +
            Text(icons['location'] + "Location", style=styles['location']) + Text("\n")
        ),
        title = Text(icons['key'] + "Keys", style=styles['guide']),
        border_style = styles['guide'],
        expand = True
    )

    cols = []
    conn_trees = {}
    metric_trees = {}
    pipes = sorted_dict(pipes)
    for conn_keys, metrics in pipes.items():
        conn_trees[conn_keys] = Tree(
            Text(
                icons['connector'] + conn_keys,
                style = styles['connector'],
            ),
            guide_style = styles['connector']
        )
        metric_trees[conn_keys] = {}
        for metric, locations in metrics.items():
            metric_trees[conn_keys][metric] = Tree(
                Text(
                    icons['metric'] + metric,
                    style = styles['metric']
                ),
                guide_style = styles['metric']
            )
            conn_trees[conn_keys].add(metric_trees[conn_keys][metric])
            for location, pipe in locations.items():
                _location = (
                    Text(str(location), style=styles['none']) if location is None
                    else Text(location, style=styles['location'])
                )
                _location = (
                    Text(icons['location'])
                    + _location + Text('\n')
                    + pipe_repr(pipe, as_rich_text=True) + Text('\n')
                )
                metric_trees[conn_keys][metric].add(_location)

    cols += [key_panel]
    for k, t in conn_trees.items():
        cols.append(t)

    columns = Columns(cols)
    get_console().print(columns)
def print_tuple(tup: tuple, skip_common: bool = True, common_only: bool = False, upper_padding: int = 0, lower_padding: int = 0) ‑> None

Print meerschaum.utils.typing.SuccessTuple.

Expand source code
def print_tuple(
        tup: tuple,
        skip_common: bool = True,
        common_only: bool = False,
        upper_padding: int = 0,
        lower_padding: int = 0,
        _progress: Optional['rich.progress.Progress'] = None,
    ) -> None:
    """Print `meerschaum.utils.typing.SuccessTuple`."""
    from meerschaum.config.static import STATIC_CONFIG
    _init()
    try:
        status = 'success' if tup[0] else 'failure'
    except TypeError:
        status = 'failure'
        tup = None, None

    omit_messages = STATIC_CONFIG['system']['success']['ignore']

    do_print = True

    if common_only:
        skip_common = False
        do_print = tup[1] in omit_messages

    if skip_common:
        do_print = tup[1] not in omit_messages

    if do_print:
        ANSI, CHARSET = __getattr__('ANSI'), __getattr__('CHARSET')
        from meerschaum.config import get_config
        status_config = get_config('formatting', status, patch=True)

        msg = ' ' + status_config[CHARSET]['icon'] + ' ' + str(tup[1])
        lines = msg.split('\n')
        lines = [lines[0]] + [
            (('    ' + line if not line.startswith(' ') else line))
            for line in lines[1:]
        ]
        if ANSI:
            lines[0] = fill_ansi(highlight_pipes(lines[0]), **status_config['ansi']['rich'])
        msg = '\n'.join(lines)
        msg = ('\n' * upper_padding) + msg + ('\n' * lower_padding)
        print(msg)
def translate_rich_to_termcolor(*colors) ‑> tuple

Translate between rich and more_termcolor terminology. This is probably prone to breaking.

Parameters

*colors :

Returns

Expand source code
def translate_rich_to_termcolor(*colors) -> tuple:
    """Translate between rich and more_termcolor terminology.
    This is probably prone to breaking.

    Parameters
    ----------
    *colors :
        

    Returns
    -------

    """
    _colors = []
    for c in colors:
        _c_list = []
        ### handle 'bright'
        c = c.replace('bright_', 'bright ')

        ### handle 'on'
        if ' on ' in c:
            _on = c.split(' on ')
            _colors.append(_on[0])
            for _c in _on[1:]:
                _c_list.append('on ' + _c)
        else:
            _c_list += [c]

        _colors += _c_list

    return tuple(_colors)