#!/usr/bin/env python3
-from abc import abstractmethod
+# © Copyright 2021-2022, Scott Gasch
+
+"""A bunch of color names mapped into RGB tuples and some methods for
+setting the text color, background, etc... using ANSI escape
+sequences.
+
+"""
+
+import contextlib
import difflib
import io
import logging
import re
import sys
-from typing import Any, Callable, Dict, Iterable, Optional, Tuple
+from abc import abstractmethod
+from typing import Any, Callable, Dict, Iterable, Literal, Optional, Tuple
from overrides import overrides
import logging_utils
+import string_utils
logger = logging.getLogger(__name__)
def is_16color(num: int) -> bool:
- return num == 255 or num == 128
+ return num in (255, 128)
def is_216color(num: int) -> bool:
b'G1szODs1OzIxbQ==\\n'
"""
- import string_utils
-
if name is not None and name == 'reset':
return '\033[39m'
green = 0
if blue is None:
blue = 0
- if (
- is_16color(red) and is_16color(green) and is_16color(blue)
- ) or force_16color:
+ if (is_16color(red) and is_16color(green) and is_16color(blue)) or force_16color:
logger.debug("Using 16-color strategy")
return fg_16color(red, green, blue)
- if (
- is_216color(red) and is_216color(green) and is_216color(blue)
- ) or force_216color:
+ if (is_216color(red) and is_216color(green) and is_216color(blue)) or force_216color:
logger.debug("Using 216-color strategy")
return fg_216color(red, green, blue)
logger.debug("Using 24-bit color strategy")
(0, 0, 0)
"""
- import string_utils
-
if name is not None and string_utils.is_full_string(name):
rgb = _find_color_by_name(name)
else:
max_ratio = r
best_guess = possibility
assert best_guess is not None
- logger.debug(f"Best guess at color name is {best_guess}")
+ logger.debug("Best guess at color name is %s", best_guess)
return best_guess
+@logging_utils.squelch_repeated_log_messages(1)
def bg(
name: Optional[str] = "",
red: Optional[int] = None,
b'G1s0ODs1OzE5Nm0=\\n'
"""
- import string_utils
-
if name is not None and name == 'reset':
return '\033[49m'
green = 0
if blue is None:
blue = 0
- if (
- is_16color(red) and is_16color(green) and is_16color(blue)
- ) or force_16color:
+ if (is_16color(red) and is_16color(green) and is_16color(blue)) or force_16color:
logger.debug("Using 16-color strategy")
return bg_16color(red, green, blue)
- if (
- is_216color(red) and is_216color(green) and is_216color(blue)
- ) or force_216color:
+ if (is_216color(red) and is_216color(green) and is_216color(blue)) or force_216color:
logger.debug("Using 216-color strategy")
return bg_216color(red, green, blue)
logger.debug("Using 24-bit color strategy")
return bg_24bit(red, green, blue)
-class StdoutInterceptor(io.TextIOBase):
+class StdoutInterceptor(io.TextIOBase, contextlib.AbstractContextManager):
+ """An interceptor for data written to stdout. Use as a context."""
+
def __init__(self):
- self.saved_stdout: Optional[io.TextIOBase] = None
+ super().__init__()
+ self.saved_stdout: io.TextIO = None
self.buf = ''
@abstractmethod
def write(self, s: str):
pass
- def __enter__(self) -> None:
+ def __enter__(self):
self.saved_stdout = sys.stdout
sys.stdout = self
- return None
+ return self
- def __exit__(self, *args) -> bool:
+ def __exit__(self, *args) -> Literal[False]:
sys.stdout = self.saved_stdout
print(self.buf)
- return None
+ return False
class ProgrammableColorizer(StdoutInterceptor):
+ """A colorizing interceptor; pass it re.Patterns -> methods that do
+ something (usually add color to) the match.
+
+ """
+
def __init__(
self,
patterns: Iterable[Tuple[re.Pattern, Callable[[Any, re.Pattern], str]]],
):
super().__init__()
- self.patterns = [_ for _ in patterns]
+ self.patterns = list(patterns)
@overrides
def write(self, s: str):
_ = pick_contrasting_color(possibility)
xf = fg(None, _[0], _[1], _[2])
xb = bg(None, _[0], _[1], _[2])
- print(
- f'{f}{xb}{possibility}{reset()}\t\t\t'
- f'{b}{xf}{possibility}{reset()}'
- )
+ print(f'{f}{xb}{possibility}{reset()}\t\t\t' f'{b}{xf}{possibility}{reset()}')
main()