dikdimon's picture
Upload extensions using SD-Hub extension
f4a41d8 verified
# Python
from __future__ import annotations
import re
from dataclasses import dataclass
from typing import ClassVar
# Lib
import colorama as c
import numpy as np
# ################################# Constants ################################ #
FALSY = ["false", "no", "0"]
TRUTHY = ["true", "yes", "1"]
DEBUG_ICON = "\U0001F41B" # 🐛
INFO_ICON = "\U0001F6C8" # 🛈
WARN_ICON = "\U000026A0" # ⚠
ERR_ICON = "\U000026D4" # ⛔
CRIT_ICON = "\U000026A1" # ⚡
# ############################# Utility Functions ############################ #
@dataclass(frozen=True)
class LogLevel:
name: str
severity: int
prefix: str
color: str
class Logger:
DEBUG: ClassVar = LogLevel("debug", 10, "[D]", c.Fore.GREEN)
INFO: ClassVar = LogLevel("info", 20, "[I]", c.Fore.BLUE)
WARN: ClassVar = LogLevel("warning", 30, "[W]", c.Fore.MAGENTA)
ERROR: ClassVar = LogLevel("error", 40, "[E]", c.Fore.RED)
CRITICAL: ClassVar = LogLevel("critical", 50, "[C]", c.Fore.YELLOW + c.Style.BRIGHT)
def __init__(self, level: LogLevel = INFO, *, color: bool = False):
self.__min_sev = level.severity
c.init(autoreset=False, strip=not color)
def __print(self, level: LogLevel, msg: str, dataset):
if level.severity >= self.__min_sev:
print(f"\n{level.color}{level.prefix} {msg}")
for sub_msg in dataset:
print(f" * {sub_msg}")
print(c.Style.RESET_ALL)
def debug(self, msg: str, dataset: list | None = None):
self.__print(Logger.DEBUG, msg, dataset or [])
def info(self, msg, dataset: list | None = None):
self.__print(Logger.INFO, msg, dataset or [])
def warn(self, msg, dataset: list | None = None):
self.__print(Logger.WARN, msg, dataset or [])
def error(self, msg, dataset: list | None = None):
self.__print(Logger.ERROR, msg, dataset or [])
# TODO: add cli args to config the logs
logger = Logger(Logger.DEBUG, color=True)
def clean_name(name: str):
return str(name).lower().strip().replace(" ", "_").replace("[", "").replace("]", "")
def get_closest_from_list(name: str, choices: list[str]) -> str:
if name in choices:
return name
found = sorted([item for item in choices if name in item], key=len)
if found:
return found[0]
return ""
re_range = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\(([+-]\d+)\s*\))?\s*")
re_range_float = re.compile(
r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\(([+-]\d+(?:.\d*)?)\s*\))?\s*"
)
re_range_count = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\[(\d+)\s*\])?\s*")
re_range_count_float = re.compile(
r"\s*([+-]?\s*\d+(?:.\d*)?)\s*-\s*([+-]?\s*\d+(?:.\d*)?)(?:\s*\[(\d+(?:.\d*)?)\s*\])?\s*"
)
def parse_range_int(value_list: list[str]) -> list[int]:
parsed_list = []
for val in value_list:
match = re_range.fullmatch(val)
count = re_range_count.fullmatch(val)
if match is not None:
start = int(match.group(1))
end = int(match.group(2)) + 1
step = int(match.group(3)) if match.group(3) is not None else 1
parsed_list += list(range(start, end, step))
elif count is not None:
start = int(count.group(1))
end = int(count.group(2))
num = int(count.group(3)) if count.group(3) is not None else 1
parsed_list += [int(x) for x in np.linspace(start=start, stop=end, num=num).tolist()]
else:
parsed_list.append(int(val))
return parsed_list
def parse_range_float(value_list: list[str]) -> list[float]:
parsed_list = []
for val in value_list:
match = re_range_float.fullmatch(val)
count = re_range_count_float.fullmatch(val)
if match is not None:
start = float(match.group(1))
end = float(match.group(2))
step = float(match.group(3)) if match.group(3) is not None else 1
parsed_list += np.arange(start, end + step, step).tolist()
elif count is not None:
start = float(count.group(1))
end = float(count.group(2))
num = int(count.group(3)) if count.group(3) is not None else 1
parsed_list += np.linspace(start=start, stop=end, num=num).tolist()
else:
parsed_list += [round(float(val), 2)]
return parsed_list