text: """
  color.py
  ========
  
  Usage
  -----
  
  >>> import color
  >>>
  >>> # 8-bit color
  >>> print(red('red') + green('green') + blue('blue'))
  >>> print(bold(yellow('bold yellow')) + underline(cyan('underline cyan')))
  >>> print(magenta_hl('magenta highlight'))
  >>>
  >>> # xterm 256 color
  >>> print(bg256('A9D5DE', fg256('276F86', 'Info!')))
  >>> print(bg256('E0B4B4', fg256('912D2B', 'Warning!')))
  >>> print(hl256('10a3a3', 'Teal'))
  
  Note:
  
  1. Every color function receives and returns string, so that the result
     could be used with any other strings, in any string formatting situation.
  
  2. If you pass a str type string, the color function will return a str.
     If you pass a bytes type string, the color function will return a bytes string.
  
  3. Color functions could be composed together, like put ``red`` into ``bold``,
     or put ``bg256`` into ``fg256``. ``xxx_hl`` and ``hl256`` are mostly used
     independently.
  
  API
  ---
  
  8-bit colors:
  
  ========  ============  ===========
   Colors    Background    Highlight
  ========  ============  ===========
  black     black_bg      black_hl
  red       red_bg        red_hl
  green     green_bg      green_hl
  yellow    yellow_bg     yellow_hl
  blue      blue_bg       blue_hl
  magenta   magenta_bg    magenta_hl
  cyan      cyan_bg       cyan_hl
  white     white_bg      white_hl
  ========  ============  ===========
  
  Styles:
  - bold
  - italic
  - underline
  - strike
  - blink
  
  .. py:function:: <color_function>(s)
  
     Decorate string with specified color or style.
  
     A color function with ``_bg`` suffix means it will set color as background.
     A color function with ``_hl`` suffix means it will set color as background,
     and change the foreground as well to make the word standout.
  
     :param str s: The input string
     :return: The decorated string
     :rtype: string
     :raises ValueError: if the message_body exceeds 160 characters
  
  
  256 colors:
  - fg256
  - bg256
  - hl256
  
  .. py:function:: <256_color_function>(hexrgb, s)
  
     Decorate string with specified hex rgb color
  
     ``fg256`` will set color as foreground.
     ``bg256`` will set color as background.
     ``hg256`` will highlight input with the color.
  
     :param str hexrgb: The hex rgb color string, accept length 3 and 6. eg: ``555``, ``912D2B``
     :param str s: The input string
     :return: The decorated string
     :rtype: string
     :raises ValueError: If the input string's length not equal to 3 or 6.
  """
  
  from typing import Union, Any, Callable, Optional, Tuple, List, Dict
  import sys
  
  
  _use_color_no_tty = True
  
  
  def use_color_no_tty(flag):
      global _use_color_no_tty
      _use_color_no_tty = flag
  
  
  def use_color():
      if sys.stdout.isatty():
          return True
      if _use_color_no_tty:
          return True
      return False
  
  
  def esc(*codes: Union[int, str]) -> str:
      """Produces an ANSI escape code from a list of integers
      :rtype: text_type
      """
      return t_('\x1b[{}m').format(t_(';').join(t_(str(c)) for c in codes))
  
  
  def t_(b: Union[bytes, Any]) -> str:
      """ensure text type"""
      if isinstance(b, bytes):
          return b.decode()
      return b
  
  
  def b_(t: Union[str, Any]) -> bytes:
      """ensure binary type"""
      if isinstance(t, str):
          return t.encode()
      return t
  
  
  ###############################################################################
  # 8 bit Color
  ###############################################################################
  
  def make_color(start, end: str) -> Callable[[str], str]:
      def color_func(s: str) -> str:
          if not use_color():
              return s
  
          # render
          return start + t_(s) + end
  
      return color_func
  
  
  # According to https://en.wikipedia.org/wiki/ANSI_escape_code#graphics ,
  # 39 is reset for foreground, 49 is reset for background, 0 is reset for all
  # we can use 0 for convenience, but it will make color combination behaves weird.
  END = esc(0)
  
  FG_END = esc(39)
  black = make_color(esc(30), FG_END)
  red = make_color(esc(31), FG_END)
  green = make_color(esc(32), FG_END)
  yellow = make_color(esc(33), FG_END)
  blue = make_color(esc(34), FG_END)
  magenta = make_color(esc(35), FG_END)
  cyan = make_color(esc(36), FG_END)
  white = make_color(esc(37), FG_END)
  
  BG_END = esc(49)
  black_bg = make_color(esc(40), BG_END)
  red_bg = make_color(esc(41), BG_END)
  green_bg = make_color(esc(42), BG_END)
  yellow_bg = make_color(esc(43), BG_END)
  blue_bg = make_color(esc(44), BG_END)
  magenta_bg = make_color(esc(45), BG_END)
  cyan_bg = make_color(esc(46), BG_END)
  white_bg = make_color(esc(47), BG_END)
  
  HL_END = esc(22, 27, 39)
  #HL_END = esc(22, 27, 0)
  
  black_hl = make_color(esc(1, 30, 7), HL_END)
  red_hl = make_color(esc(1, 31, 7), HL_END)
  green_hl = make_color(esc(1, 32, 7), HL_END)
  yellow_hl = make_color(esc(1, 33, 7), HL_END)
  blue_hl = make_color(esc(1, 34, 7), HL_END)
  magenta_hl = make_color(esc(1, 35, 7), HL_END)
  cyan_hl = make_color(esc(1, 36, 7), HL_END)
  white_hl = make_color(esc(1, 37, 7), HL_END)
  
  bold = make_color(esc(1), esc(22))
  italic = make_color(esc(3), esc(23))
  underline = make_color(esc(4), esc(24))
  strike = make_color(esc(9), esc(29))
  blink = make_color(esc(5), esc(25))
  
  
  ###############################################################################
  # Xterm 256 Color (delete if you don't need)
  ###############################################################################
  #
  # Rewrite from: https://gist.github.com/MicahElliott/719710
  
  import re  # NOQA
  
  # Default color levels for the color cube
  CUBELEVELS: List[int] = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
  
  # Generate a list of midpoints of the above list
  SNAPS: List[int] = [(x + y) // 2 for x, y in list(zip(CUBELEVELS, [0] + CUBELEVELS))[1:]]
  
  # Gray-scale range.
  _GRAYSCALE = [
      (0x08, 232),  # 0x08 means 080808 in HEX color
      (0x12, 233),
      (0x1c, 234),
      (0x26, 235),
      (0x30, 236),
      (0x3a, 237),
      (0x44, 238),
      (0x4e, 239),
      (0x58, 240),
      (0x62, 241),
      (0x6c, 242),
      (0x76, 243),
      (0x80, 244),
      (0x8a, 245),
      (0x94, 246),
      (0x9e, 247),
      (0xa8, 248),
      (0xb2, 249),
      (0xbc, 250),
      (0xc6, 251),
      (0xd0, 252),
      (0xda, 253),
      (0xe4, 254),
      (0xee, 255),
  ]
  GRAYSCALE: Dict[int, int] = dict(_GRAYSCALE)
  
  GRAYSCALE_POINTS: List[int] = [i for i, _ in _GRAYSCALE]
  
  
  def get_closest(v: int, l: list):
      return min(l, key=lambda x: abs(x - v))
  
  
  class Memorize(dict):
      def __init__(self, func):
          self.func = func
          self.__doc__ = func.__doc__
  
      def __call__(self, *args):
          return self[args]
  
      def __missing__(self, key):
          result = self[key] = self.func(*key)
          return result
  
  
  def memorize(func) -> Callable:
      func._cache = {}
  
      def wrapper(*args, **kwargs):
          if kwargs:
              return func(*args, **kwargs)
          if args not in func._cache:
              func._cache[args] = func(*args, **kwargs)
          return func._cache[args]
  
      for i in ('__module__', '__name__', '__doc__'):
          setattr(wrapper, i, getattr(func, i))
      wrapper.__dict__.update(getattr(func, '__dict__', {}))  # type: ignore
      wrapper._origin = func  # type: ignore
      return wrapper
  
  
  @memorize
  def rgb_to_xterm(r: int, g: int, b: int) -> int:
      """ Converts RGB values to the nearest equivalent xterm-256 color.
      """
      if r == g == b:
          # use gray scale
          gs = get_closest(r, GRAYSCALE_POINTS)
          return GRAYSCALE[gs]
      # Using list of snap points, convert RGB value to cube indexes
      r, g, b = map(lambda x: len(tuple(s for s in SNAPS if s < x)), (r, g, b))
      # Simple colorcube transform
      return r * 36 + g * 6 + b + 16
  
  
  @memorize
  def hex_to_rgb(hx: str) -> Tuple[int, int, int]:
      hxlen = len(hx)
      if hxlen != 3 and hxlen != 6:
          raise ValueError('hx color must be of length 3 or 6')
      if hxlen == 3:
          hx = t_('').join(i * 2 for i in hx)
      parts = [int(h, 16) for h in re.split(t_(r'(..)(..)(..)'), hx)[1:4]]
      return tuple(parts)  # type: ignore
  
  
  def make_256(start: str, end: str) -> Callable[[Union[tuple, str], str, Optional[Tuple[int, int, int]]], str]:
      def rgb_func(rgb: Union[tuple, str], s: str, x: Optional[Tuple[int, int, int]] = None) -> str:
          """
          :param rgb: (R, G, B) tuple, or RRGGBB hex string
          """
          if not use_color():
              return s
  
          t = t_(s)
  
          # render
          if not isinstance(rgb, tuple):
              rgb = hex_to_rgb(t_(rgb))
          if x is not None:
              xcolor = x
          else:
              xcolor = rgb_to_xterm(*rgb)
  
          tpl = start + t_('{s}') + end
          f = tpl.format(
              x=xcolor,
              s=t)
  
          return f
  
      return rgb_func
  
  
  fg256 = make_256(esc(38, 5, t_('{x}')), esc(39))
  bg256 = make_256(esc(48, 5, t_('{x}')), esc(49))
  hl256 = make_256(esc(1, 38, 5, t_('{x}'), 7), esc(27, 39, 22))
  
  _grayscale_xterm_codes = [i for _, i in _GRAYSCALE]
  grayscale = {(i - _grayscale_xterm_codes[0]): make_color(esc(38, 5, i), esc(39)) for i in _grayscale_xterm_codes}
  grayscale_bg = {(i - _grayscale_xterm_codes[0]): make_color(esc(48, 5, i), esc(49)) for i in _grayscale_xterm_codes}
  grayscale_hl = {(i - _grayscale_xterm_codes[0]): make_color(esc(1, 38, 5, i, 7), esc(27, 39, 22)) for i in _grayscale_xterm_codes}
  