213 lines
5.8 KiB
Python
213 lines
5.8 KiB
Python
# Copyright (c) 2008-2011 Volvox Development Team
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
#
|
|
# Author: Konstantin Lepa <konstantin.lepa@gmail.com>
|
|
|
|
"""ANSI color formatting for output in terminal."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
import os
|
|
import sys
|
|
import warnings
|
|
from typing import Any, Iterable
|
|
|
|
from ._types import Attribute, Color, Highlight
|
|
|
|
|
|
def __getattr__(name: str) -> list[str]:
|
|
if name == "__ALL__":
|
|
warnings.warn(
|
|
"__ALL__ is deprecated and will be removed in termcolor 3. "
|
|
"Use __all__ instead.",
|
|
DeprecationWarning,
|
|
stacklevel=2,
|
|
)
|
|
return ["colored", "cprint"]
|
|
msg = f"module '{__name__}' has no attribute '{name}'"
|
|
raise AttributeError(msg)
|
|
|
|
|
|
ATTRIBUTES: dict[Attribute, int] = {
|
|
"bold": 1,
|
|
"dark": 2,
|
|
"underline": 4,
|
|
"blink": 5,
|
|
"reverse": 7,
|
|
"concealed": 8,
|
|
}
|
|
|
|
HIGHLIGHTS: dict[Highlight, int] = {
|
|
"on_black": 40,
|
|
"on_grey": 40, # Actually black but kept for backwards compatibility
|
|
"on_red": 41,
|
|
"on_green": 42,
|
|
"on_yellow": 43,
|
|
"on_blue": 44,
|
|
"on_magenta": 45,
|
|
"on_cyan": 46,
|
|
"on_light_grey": 47,
|
|
"on_dark_grey": 100,
|
|
"on_light_red": 101,
|
|
"on_light_green": 102,
|
|
"on_light_yellow": 103,
|
|
"on_light_blue": 104,
|
|
"on_light_magenta": 105,
|
|
"on_light_cyan": 106,
|
|
"on_white": 107,
|
|
}
|
|
|
|
COLORS: dict[Color, int] = {
|
|
"black": 30,
|
|
"grey": 30, # Actually black but kept for backwards compatibility
|
|
"red": 31,
|
|
"green": 32,
|
|
"yellow": 33,
|
|
"blue": 34,
|
|
"magenta": 35,
|
|
"cyan": 36,
|
|
"light_grey": 37,
|
|
"dark_grey": 90,
|
|
"light_red": 91,
|
|
"light_green": 92,
|
|
"light_yellow": 93,
|
|
"light_blue": 94,
|
|
"light_magenta": 95,
|
|
"light_cyan": 96,
|
|
"white": 97,
|
|
}
|
|
|
|
|
|
RESET = "\033[0m"
|
|
|
|
|
|
def _can_do_colour(
|
|
*, no_color: bool | None = None, force_color: bool | None = None
|
|
) -> bool:
|
|
"""Check env vars and for tty/dumb terminal"""
|
|
# First check overrides:
|
|
# "User-level configuration files and per-instance command-line arguments should
|
|
# override $NO_COLOR. A user should be able to export $NO_COLOR in their shell
|
|
# configuration file as a default, but configure a specific program in its
|
|
# configuration file to specifically enable color."
|
|
# https://no-color.org
|
|
if no_color is not None and no_color:
|
|
return False
|
|
if force_color is not None and force_color:
|
|
return True
|
|
|
|
# Then check env vars:
|
|
if "ANSI_COLORS_DISABLED" in os.environ:
|
|
return False
|
|
if "NO_COLOR" in os.environ:
|
|
return False
|
|
if "FORCE_COLOR" in os.environ:
|
|
return True
|
|
|
|
# Then check system:
|
|
if os.environ.get("TERM") == "dumb":
|
|
return False
|
|
if not hasattr(sys.stdout, "fileno"):
|
|
return False
|
|
|
|
try:
|
|
return os.isatty(sys.stdout.fileno())
|
|
except io.UnsupportedOperation:
|
|
return sys.stdout.isatty()
|
|
|
|
|
|
def colored(
|
|
text: object,
|
|
color: Color | None = None,
|
|
on_color: Highlight | None = None,
|
|
attrs: Iterable[Attribute] | None = None,
|
|
*,
|
|
no_color: bool | None = None,
|
|
force_color: bool | None = None,
|
|
) -> str:
|
|
"""Colorize text.
|
|
|
|
Available text colors:
|
|
black, red, green, yellow, blue, magenta, cyan, white,
|
|
light_grey, dark_grey, light_red, light_green, light_yellow, light_blue,
|
|
light_magenta, light_cyan.
|
|
|
|
Available text highlights:
|
|
on_black, on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white,
|
|
on_light_grey, on_dark_grey, on_light_red, on_light_green, on_light_yellow,
|
|
on_light_blue, on_light_magenta, on_light_cyan.
|
|
|
|
Available attributes:
|
|
bold, dark, underline, blink, reverse, concealed.
|
|
|
|
Example:
|
|
colored('Hello, World!', 'red', 'on_black', ['bold', 'blink'])
|
|
colored('Hello, World!', 'green')
|
|
"""
|
|
result = str(text)
|
|
if not _can_do_colour(no_color=no_color, force_color=force_color):
|
|
return result
|
|
|
|
fmt_str = "\033[%dm%s"
|
|
if color is not None:
|
|
result = fmt_str % (COLORS[color], result)
|
|
|
|
if on_color is not None:
|
|
result = fmt_str % (HIGHLIGHTS[on_color], result)
|
|
|
|
if attrs is not None:
|
|
for attr in attrs:
|
|
result = fmt_str % (ATTRIBUTES[attr], result)
|
|
|
|
result += RESET
|
|
|
|
return result
|
|
|
|
|
|
def cprint(
|
|
text: object,
|
|
color: Color | None = None,
|
|
on_color: Highlight | None = None,
|
|
attrs: Iterable[Attribute] | None = None,
|
|
*,
|
|
no_color: bool | None = None,
|
|
force_color: bool | None = None,
|
|
**kwargs: Any,
|
|
) -> None:
|
|
"""Print colorized text.
|
|
|
|
It accepts arguments of print function.
|
|
"""
|
|
|
|
print(
|
|
(
|
|
colored(
|
|
text,
|
|
color,
|
|
on_color,
|
|
attrs,
|
|
no_color=no_color,
|
|
force_color=force_color,
|
|
)
|
|
),
|
|
**kwargs,
|
|
)
|