KiBot/kibot/mcpyrate/colorizer.py

164 lines
5.8 KiB
Python

# -*- coding: utf-8; -*-
"""Colorize terminal output.
Use Colorama if available; works on any OS.
If not available, and OS is a *nix, use ANSI escape codes.
"""
__all__ = ["setcolor", "colorize", "ColorScheme",
"Fore", "Back", "Style"]
try:
from colorama import init as colorama_init, Fore, Back, Style
colorama_init()
except ImportError: # pragma: no cover
# The `ansi` module is a slightly modified, POSIX-only,
# vendored version from Colorama. Useful e.g. in Docker
# images that don't have the library available.
from .ansi import Fore, Back, Style # noqa: F811
from .bunch import Bunch
def setcolor(*colors, reset=True):
"""Set color for terminal display.
Returns a string that, when printed into a terminal, sets the style
and color.
If `reset=True`, reset style and color before setting the requested
style and color.
If `reset=False`, augment current style and color. E.g. if `Style.BRIGHT`
is already active, and you set `Fore.BLUE`, the style will remain BRIGHT,
and color will be BLUE.
For available `colors`, see `Fore`, `Back` and `Style`.
Each entry can also be a tuple (arbitrarily nested), which is useful
for defining compound styles.
**CAUTION**: The specified style and color remain in effect until another
explicit call to `setcolor`. To reset, use `setcolor()`.
If you want to colorize a piece of text so that the color and style
auto-reset after your text, use `colorize` instead.
"""
def _setcolor(color):
if isinstance(color, (list, tuple)):
return "".join(_setcolor(elt) for elt in color)
return color
out = [_setcolor(Style.RESET_ALL)] if reset else []
out.append(_setcolor(colors))
return "".join(out)
def colorize(text, *colors):
"""Colorize string `text` for terminal display.
Always reset style and color at the start of `text`, as well as after it.
Returns `text`, augmented with color and style commands for terminals.
For available `colors`, see `Fore`, `Back` and `Style`.
Usage::
print(colorize("I'm new here", Fore.GREEN))
print(colorize("I'm bold and bluetiful", Style.BRIGHT, Fore.BLUE))
Each entry can also be a tuple (arbitrarily nested), which is useful
for defining compound styles::
BRIGHT_BLUE = (Style.BRIGHT, Fore.BLUE)
...
print(colorize("I'm bold and bluetiful, too", BRIGHT_BLUE))
**CAUTION**: Does not nest. If you want to set a color and style
until further notice, use `setcolor` instead.
"""
return "{}{}{}".format(setcolor(colors),
text,
setcolor())
class ColorScheme(Bunch):
"""The color scheme for terminal output in `mcpyrate`'s debug utilities.
This is just a bunch of constants. To change the colors, simply assign new
values to them. Changes take effect immediately for any new output.
To replace the whole color scheme at once, fill in a suitable `Bunch`, and
then use the `replace` method. If you need to get the names of all settings
programmatically, call the `keys` method.
Don't replace the color scheme object itself; all the use sites
from-import it.
See `Fore`, `Back`, `Style` for valid values. To make a compound style,
place the values into a tuple.
The defaults are designed to fit the "Solarized" (Zenburn-like) theme
of `gnome-terminal`, with "Show bold text in bright colors" set to OFF.
But they work also with "Tango", and indeed with most themes.
"""
def __init__(self):
super().__init__()
# ------------------------------------------------------------
# unparse
self.LINENUMBER = Style.DIM
self.LANGUAGEKEYWORD = (Style.BRIGHT, Fore.YELLOW) # for, if, import, ...
self.BUILTINEXCEPTION = Fore.CYAN # TypeError, ValueError, Warning, ...
self.BUILTINOTHER = Style.BRIGHT # str, property, print, ...
self.DEFNAME = (Style.BRIGHT, Fore.CYAN) # name of a function or class being defined
self.DECORATOR = Fore.LIGHTBLUE_EX
# These can be highlighted differently although Python 3.8+ uses `Constant` for all.
self.STRING = Fore.GREEN
self.NUMBER = Fore.GREEN
self.NAMECONSTANT = Fore.GREEN # True, False, None
# Macro names are syntax-highlighted when a macro expander instance is
# running and is provided to `unparse`, so it can query for bindings.
# `step_expansion` does that automatically.
#
# So they won't yet be highlighted during dialect AST transforms,
# because at that point, there is no *macro* expander.
self.MACRONAME = Fore.BLUE
self.INVISIBLENODE = Style.DIM # AST node with no surface syntax repr (`Module`, `Expr`)
# AST markers for data-driven communication within the macro expander
self.ASTMARKER = Style.DIM # the "$AstMarker" title
self.ASTMARKERCLASS = Fore.YELLOW # the actual marker type name
# ------------------------------------------------------------
# format_bindings, step_expansion, StepExpansion
self.HEADING = (Style.BRIGHT, Fore.LIGHTBLUE_EX)
self.SOURCEFILENAME = Style.BRIGHT
# format_bindings
self.GREYEDOUT = Style.DIM # if no bindings
# step_expansion
self.TREEID = Fore.LIGHTBLUE_EX
# StepExpansion
self.ATTENTION = (Style.BRIGHT, Fore.GREEN) # "DialectExpander debug mode"
self.TRANSFORMERKIND = (Style.BRIGHT, Fore.GREEN) # "source", "AST"
self.DIALECTTRANSFORMERNAME = (Style.BRIGHT, Fore.YELLOW)
# ------------------------------------------------------------
# dump
self.NODETYPE = (Style.BRIGHT, Fore.LIGHTBLUE_EX)
self.FIELDNAME = Fore.YELLOW
self.BAREVALUE = Fore.GREEN
ColorScheme = ColorScheme()