Source code for lore.ansi

# -*- coding: utf-8 -*-
"""
ANSI makes it easy!
*******************

:any:`lore.ansi` makes formatting text output super simple! Lore doesn't have
much of a UI. Text output should be excellent.

.. role:: strike
    :class: strike
"""
from __future__ import absolute_import, print_function, unicode_literals

import platform

RESET = 0  #: Game over!

BOLD = 1  #: For people with a heavy hand.
FAINT = 2  #: For people with a light touch.
ITALIC = 3  #: Are Italian's emphatic, or is Italy slanted? `Etymology <https://www.etymonline.com/word/italic>`_ is fun.
UNDERLINE = 4  #: It's got a line under it, no need for a PhD here.
STROBE = 5  #: For sadists looking to cause seizures. Doesn't work except on masochist's platforms.
BLINK = 6  #: For that patiently waiting cursor effect. Also doesn't work, since sadists ruined it for everyone.
INVERSE = 7  #: Today is backwards day.
CONCEAL = 8  #: Why would you do this‽ Your attempt has been logged, and will be reported to the authorities.
STRIKE = 9  #: Adopt that sense of humility, let other people know you w̶e̶r̶e̶ ̶w̶r̶o̶n̶g learned from experience.

BLACK = 30  #: If you gaze long into an abyss, the abyss will gaze back into you.
RED = 31  #: Hot and loud. Like a fire engine, anger, or you've just bitten your cheek for the third time today.
GREEN = 32  #: The most refreshingly natural color. Growth and softness.
YELLOW = 33  #: Daisies and deadly poison dart frogs. Salted butter and lightning. Like scotch filtered through gold foil.
BLUE = 34  #: Skies, oceans, infinite depths. The color of hope and melancholy.
MAGENTA = 35  #: For latin salsa dresses with matching shoes. Also, the radiant color of T brown dwarf stars as long as sodium and potassium atoms absorb the :any:`GREEN` light in the spectrum.
CYAN = 36  #: Only printers who prefer CMYK over RGB would name this color. It's :any:`BLUE` stripped of soul, injected with 10,000 volts. A true Frankenstein's monster.
WHITE = 37  #: The sum of all colors, to the point there is no color left at all. Floats nicely in the abyss.
DEFAULT = 39  #: You get 3 guesses what this color is, and the first 2 don't count.


if platform.system() == 'Windows':
    ###
    # This is an unfortunate hack to enable ansi output on Windows
    # https://bugs.python.org/issue30075
    import msvcrt
    import ctypes
    import os

    from ctypes import wintypes # pylint: disable=ungrouped-imports

    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

    ERROR_INVALID_PARAMETER = 0x0057
    ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004

    def _check_bool(result, func, args):
        if not result:
            raise ctypes.WinError(ctypes.get_last_error())
        return args

    LPDWORD = ctypes.POINTER(wintypes.DWORD)
    kernel32.GetConsoleMode.errcheck = _check_bool
    kernel32.GetConsoleMode.argtypes = (wintypes.HANDLE, LPDWORD)
    kernel32.SetConsoleMode.errcheck = _check_bool
    kernel32.SetConsoleMode.argtypes = (wintypes.HANDLE, wintypes.DWORD)

    def set_conout_mode(new_mode, mask=0xffffffff):
        # don't assume StandardOutput is a console.
        # open CONOUT$ instead
        fdout = os.open('CONOUT$', os.O_RDWR)
        try:
            hout = msvcrt.get_osfhandle(fdout)
            old_mode = wintypes.DWORD()
            kernel32.GetConsoleMode(hout, ctypes.byref(old_mode))
            mode = (new_mode & mask) | (old_mode.value & ~mask)
            kernel32.SetConsoleMode(hout, mode)
            return old_mode.value
        finally:
            os.close(fdout)

    def enable_vt_mode():
        mode = mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING
        try:
            return set_conout_mode(mode, mask)
        except WindowsError as e:  # pylint: disable=undefined-variable
            if e.winerror == ERROR_INVALID_PARAMETER:
                raise NotImplementedError
            raise
    import atexit
    atexit.register(set_conout_mode, enable_vt_mode())


[docs]def debug(content='DEBUG'): """ debug style :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return gray(7, content)
[docs]def info(content='INFO'): """ info style :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return foreground(BLUE, content)
[docs]def warning(content='WARNING'): """ warning style :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return foreground(MAGENTA, content)
[docs]def success(content='SUCCESS'): """ success style :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return foreground(GREEN, content)
[docs]def error(content='ERROR'): """ error style :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return foreground(RED, content)
[docs]def critical(content='CRITICAL'): """ for really big fuck ups, not to be used lightly. :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return blink(foreground(bright(RED), content))
[docs]def foreground(color, content, readline=False): """ Color the text of the content :param color: pick a constant, any constant :type color: int :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return encode(color, readline=readline) + content + encode(DEFAULT, readline=readline)
[docs]def background(color, content): """ Color the background of the content :param color: pick a constant, any constant :type color: int :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return encode(color + 10) + content + encode(DEFAULT + 10)
[docs]def bright(color): """ Brighten a color :param color: pick a constant, any constant :type color: int :type content: unicode :return: brighter version of the color :rtype: unicode """ return color + 60
[docs]def gray(level, content): """ Grayscale :param level: [0-15] 0 is almost black, 15 is nearly white :type level: int :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return encode('38;5;%i' % (232 + level)) + content + encode(DEFAULT)
[docs]def rgb(red, green, blue, content): """ Colors a content using rgb for h :param red: [0-5] :type red: int :param green: [0-5] :type green: int :param blue: [0-5] :type blue: int :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ color = 16 + 36 * red + 6 * green + blue return encode('38;5;' + str(color)) + content + encode(DEFAULT)
[docs]def bold(content): """ Bold content :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return style(BOLD, content)
[docs]def faint(content): """ Faint content :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return style(FAINT, content)
[docs]def italic(content): """ Italic content :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return style(ITALIC, content)
[docs]def underline(content): """ Underline content :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return style(UNDERLINE, content)
[docs]def strobe(content): """ Quickly blinking content :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return style(STROBE, content)
[docs]def inverse(content): """ Inverted content :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return style(BLINK, content)
[docs]def conceal(content): """ Why do you persist in this nonsense? :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return style(CONCEAL, content)
[docs]def strike(content): """ Strike through content :param content: Whatever you want to say... :type content: unicode :return: ansi string :rtype: unicode """ return style(STRIKE, content)
[docs]def reset(): """ Remove any active ansi styles :return: string resetting marker :rtype: unicode """ return encode(RESET)
[docs]def style(effect, content): """ add a particular style to the content :param effect: style :type effect: int :param content: Whatever you want to say... string :type content: unicode :return: ansi string :rtype: unicode """ return encode(effect) + content + encode(RESET)
[docs]def encode(code, readline=False): """ Adds escape and control characters for ANSI codes :param code: pick a constant, any constant :type code: int :param readline: add readline compatibility, which causes bugs in other formats :type content: unicode :return: ansi string :rtype: unicode """ if readline: return '\001\033[' + str(code) + 'm\002' return '\033[' + str(code) + 'm'