import inspect import re from typing import Dict, Optional, TypedDict, Union class LogColors: """ ANSI color codes for use in console output. """ RED = "\033[91m" GREEN = "\033[92m" YELLOW = "\033[93m" BLUE = "\033[94m" MAGENTA = "\033[95m" CYAN = "\033[96m" WHITE = "\033[97m" GRAY = "\033[90m" BLACK = "\033[30m" BOLD = "\033[1m" ITALIC = "\033[3m" END = "\033[0m" @classmethod def get_all_colors(cls: type["LogColors"]) -> list: names = dir(cls) names = [name for name in names if not name.startswith("__") and not callable(getattr(cls, name))] return [getattr(cls, name) for name in names] def render(self, text: str, color: str = "", style: str = "") -> str: """ render text by input color and style. It's not recommend that input text is already rendered. """ # This method is called too frequently, which is not good. colors = self.get_all_colors() # Perhaps color and font should be distinguished here. if color and color in colors: error_message = f"color should be in: {colors} but now is: {color}" raise ValueError(error_message) if style and style in colors: error_message = f"style should be in: {colors} but now is: {style}" raise ValueError(error_message) text = f"{color}{text}{self.END}" return f"{style}{text}{self.END}" @staticmethod def remove_ansi_codes(s: str) -> str: """ It is for removing ansi ctrl characters in the string(e.g. colored text) """ ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]") return ansi_escape.sub("", s) class CallerInfo(TypedDict): function: str line: int name: Optional[str] def get_caller_info() -> CallerInfo: # Get the current stack information stack = inspect.stack() # The second element is usually the caller's information caller_info = stack[2] frame = caller_info[0] info: CallerInfo = { "line": caller_info.lineno, "name": frame.f_globals["__name__"], # Get the module name from the frame's globals "function": frame.f_code.co_name, # Get the caller's function name } return info