#!/usr/bin/env python3 # -*- coding: utf-8 -*- from typing import Any, Self, Optional, Sequence, Callable from re import Match as REMatch from inspect import FrameInfo, stack as get_stack from Utils.Patterns import RE from json import dumps as json_encode, loads as json_decode class Utils: SPECIAL_REGULAR_EXPRESSION_CHARACTERS:dict[str, str] = { "\r" : "r", "\n" : "n", "\t" : "t" } @classmethod def get_dictionary(cls:type[Self], *items:Sequence[Any|None]) -> dict[str, Any|None]: dictionary:dict[str, Any|None] = {} item:Any|None for item in items: if isinstance(item, dict): dictionary.update(item) elif isinstance(item, (list, tuple)): subitem:Any|None for subitem in item: dictionary.update(cls.get_dictionary(subitem)) return dictionary @classmethod def get_keys(cls:type[Self], *items:Sequence[Any|None]) -> list[str]: keys:list[str] = [] item:Any|None for item in items: if isinstance(item, str): RE.KEY.match(item) and keys.append(item) elif isinstance(item, (list, tuple)): subitem:Any|None for subitem in item: keys.extend(cls.get_keys(subitem)) return keys @classmethod def get_dictionaries(cls:type[Self], *items:Sequence[Any|None]) -> list[dict[str, Any|None]]: dictionaries:list[dict[str, Any|None]] = [] item:Any|None for item in items: if isinstance(item, dict): dictionaries.append(item) elif isinstance(item, (list, tuple)): subitem:Any|None for subitem in item: dictionaries.extend(cls.get_dictionaries(subitem)) return dictionaries @classmethod def get_value(cls:type[Self], keys:str|Sequence[str], inputs:dict[str, Any|None]|Sequence[Any|None], default:Optional[Any] = None ) -> Any|None: if len(cls.get_keys(keys)): dictionary:dict[str, Any|None] for dictionary in cls.get_dictionaries(inputs): key:str for key in cls.get_keys(keys): if key in dictionary: return dictionary[key] return default @staticmethod def get_list(item:Any|None) -> list[Any|None]: return item if isinstance(item, (list, tuple)) else [item] @classmethod def string_variables(cls:type[Self], string:str, inputs:dict[str, Any|None]|Sequence[Any|None], default:Optional[str] = None ) -> str: variables:dict[str, Any|None] = cls.get_dictionary(inputs) def callback(matches:REMatch) -> str: key:str = matches.group(1) return ( str(variables[key]) if key in variables else default if default is not None else matches.group(0)) return RE.STRING_VARIABLE.sub(callback, string) @classmethod def get_texts(cls:type[Self], *items:list[Any|None]) -> list[str]: texts:list[str] = [] item:Any|None for item in items: if isinstance(item, str): texts.append(item) elif isinstance(item, (list, tuple)): subitem:Any|None for subitem in item: texts.extend(cls.get_texts(subitem)) return texts @staticmethod def get_action_data(i:int = 0) -> dict[str, str|int]: stack:FrameInfo = get_stack()[i] return { "file" : stack.filename, "method" : stack.function, "line" : stack.lineno } @classmethod def to_regular_expression(cls:type[Self], string:str) -> str: def callback(matches:REMatch) -> str: character:str = matches.group(0) return "\\" + ( cls.SPECIAL_REGULAR_EXPRESSION_CHARACTERS[character] if character in cls.SPECIAL_REGULAR_EXPRESSION_CHARACTERS else character) return RE.TO_REGULAR_EXPRESSION.sub(callback, string) @staticmethod def json_decode(data:str) -> Any|None: try: return json_decode(data) except Exception as _: pass return None @staticmethod def json_encode(data:Sequence[Any|None]|dict[str, Any|None]) -> str|None: try: return json_encode(data) except Exception as _: pass return None @staticmethod def to_snake(string:str) -> str: def callback(matches:REMatch) -> str: upper:str|None = matches.group(1) return "_" + upper.lower() if upper else "_" return RE.TO_SNAKE.sub(callback, string).lower() @staticmethod def get_function(string:str, _from:list[str] = []) -> Optional[Callable[[Any|None], Any|None]]: item:Any|None fragments:list[str] = string.split(".") fragment:str done:bool for item in _from: done = True for fragment in fragments: if done := hasattr(item, fragment): item = getattr(item, fragment) else: break if done and callable(item): return item if done := fragments[0] in globals(): item = globals()[fragments[0]] for fragment in fragments[1:]: if done := hasattr(item, fragment): item = getattr(item, fragment) else: break if done and callable(item): return item return None