123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- """ANSI color formatting for output in terminal."""
- from __future__ import annotations
- import io
- import os
- import sys
- from functools import reduce
- from typing import Iterable, Literal
- from reflex.utils.decorator import once
- _Attribute = Literal[
- "bold",
- "dark",
- "italic",
- "underline",
- "slow_blink",
- "rapid_blink",
- "reverse",
- "concealed",
- "strike",
- ]
- _ATTRIBUTES: dict[_Attribute, int] = {
- "bold": 1,
- "dark": 2,
- "italic": 3,
- "underline": 4,
- "slow_blink": 5,
- "rapid_blink": 6,
- "reverse": 7,
- "concealed": 8,
- "strike": 9,
- }
- _Color = Literal[
- "black",
- "red",
- "green",
- "yellow",
- "blue",
- "magenta",
- "cyan",
- "light_grey",
- "dark_grey",
- "light_red",
- "light_green",
- "light_yellow",
- "light_blue",
- "light_magenta",
- "light_cyan",
- "white",
- ]
- _COLORS: dict[_Color, int] = {
- "black": 30,
- "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,
- }
- _BackgroundColor = Literal[
- "on_black",
- "on_red",
- "on_green",
- "on_yellow",
- "on_blue",
- "on_magenta",
- "on_cyan",
- "on_light_grey",
- "on_dark_grey",
- "on_light_red",
- "on_light_green",
- "on_light_yellow",
- "on_light_blue",
- "on_light_magenta",
- "on_light_cyan",
- "on_white",
- ]
- BACKGROUND_COLORS: dict[_BackgroundColor, int] = {
- "on_black": 40,
- "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,
- }
- _ANSI_CODES = _ATTRIBUTES | BACKGROUND_COLORS | _COLORS
- _RESET_MARKER = "\033[0m"
- @once
- def _can_colorize() -> bool:
- """Check if the output can be colorized.
- Copied from _colorize.can_colorize.
- https://raw.githubusercontent.com/python/cpython/refs/heads/main/Lib/_colorize.py
- Returns:
- If the output can be colorized
- """
- file = sys.stdout
- if not sys.flags.ignore_environment:
- if os.environ.get("PYTHON_COLORS") == "0":
- return False
- if os.environ.get("PYTHON_COLORS") == "1":
- return True
- if os.environ.get("NO_COLOR"):
- return False
- if os.environ.get("FORCE_COLOR"):
- return True
- if os.environ.get("TERM") == "dumb":
- return False
- if not hasattr(file, "fileno"):
- return False
- if sys.platform == "win32":
- try:
- import nt
- if not nt._supports_virtual_terminal():
- return False
- except (ImportError, AttributeError):
- return False
- try:
- return os.isatty(file.fileno())
- except io.UnsupportedOperation:
- return file.isatty()
- def _format_str(text: str, ansi_escape_code: int | None) -> str:
- """Format text with ANSI escape code.
- Args:
- text: Text to format
- ansi_escape_code: ANSI escape code
- Returns:
- Formatted text
- """
- if ansi_escape_code is None:
- return text
- return f"\033[{ansi_escape_code}m{text}"
- def colored(
- text: object,
- color: _Color | None = None,
- background_color: _BackgroundColor | None = None,
- attrs: Iterable[_Attribute] = (),
- ) -> str:
- """Colorize text for terminal output.
- Args:
- text: Text to colorize
- color: Text color
- background_color: Background color
- attrs: Text attributes
- Returns:
- Colorized text
- """
- result = str(text)
- if not _can_colorize():
- return result
- ansi_codes_to_apply = [
- _ANSI_CODES.get(x)
- for x in [
- color,
- background_color,
- *attrs,
- ]
- if x
- ]
- return (
- reduce(_format_str, ansi_codes_to_apply, result) + _RESET_MARKER
- if ansi_codes_to_apply
- else result
- )
|