#!/usr/bin/env python3 # -*- coding: utf-8 -*- from typing import Any, Self, Optional from inspect import stack as get_stack, FrameInfo from traceback import format_stack as trace_format_stack, extract_tb as extract_traceback from json import loads as json_decode, dumps as json_encode from re import Match as REMatch from Utils.Patterns import RE class Utils: @classmethod def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]: keys:list[str] = [] item:Any|None for item in items: if isinstance(item, (list, tuple)): keys += cls.get_keys(*item) elif isinstance(item, str) and RE.KEY.match(item): keys += [item] return keys @classmethod def get_dictionaries(cls:type[Self], *items:list[Any|None]) -> list[dict[str, Any|None]]: dictionaries:list[dict[str, Any|None]] = [] item:Any|None for item in items: if isinstance(item, (list, tuple)): dictionaries += cls.get_dictionaries(*item) elif isinstance(item, dict): dictionaries += [item] return dictionaries @classmethod def get_dictionary(cls:type[Self], *items:list[Any|None]) -> dict[str, Any|None]: dictionary:dict[str, Any|None] = {} for item in items: if isinstance(item, (list, tuple)): dictionary = {**cls.get_dictionary(*item), **dictionary} elif isinstance(item, dict): dictionary = {**item, **dictionary} return dictionary @classmethod def get_value(cls:type[Self], keys:str|list[str]|tuple[str, ...], inputs:dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...], default:Optional[Any] = None ) -> Any|None: if len(keys := cls.get_keys(keys)): subinputs:dict[str, Any|None] for subinputs in cls.get_dictionaries(inputs): for key in keys: if key in subinputs: return subinputs[key] return default @classmethod def get_strings(cls:type[Self], *items:list[Any|None]) -> list[str]: strings:list[str] = [] item:Any|None for item in items: if isinstance(item, (list, tuple)): strings += cls.get_strings(*item) elif isinstance(item, str): strings += [item] return strings @classmethod def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]: keys:list[str] = [] item:Any|None for item in items: if isinstance(item, (list, tuple)): keys += cls.get_keys(*item) elif isinstance(item, str) and RE.KEY.match(item): keys += [item] return keys @classmethod def string_variables(cls:type[Self], string:str, variables:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None, ...]] = None, default:str|None = None ) -> str: variables = cls.get_dictionary(variables) def callback(matches:REMatch) -> str: key:str = matches.group(1) if key in variables: return str(variables[key]) return matches.group(0) if default is None else default return RE.STRING_VARIABLE.sub(callback, string) @staticmethod def json_decode(data:str) -> dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]|None: try: return json_decode(data) except Exception as exception: pass return None @staticmethod def json_encode(data:dict[str, Any|None]|tuple[Any|None, ...]|list[Any|None]|None) -> str|None: try: return json_encode(data) except Exception as exception: pass return None @staticmethod def get_from( item:list[Any|None]|tuple[Any|None, ...], indexes:int|list[int]|tuple[int, ...], default:Optional[Any] = None ) -> Any|None: l:int = len(item) i:int for i in indexes if isinstance(indexes, (list, tuple)) else (indexes,): if i >= l: break if item[i] is not None: return item[i] return default @staticmethod def get_action_data(i:int = 0) -> dict[str, str|int]: stack:FrameInfo = get_stack()[1 + i] return { "file" : stack.filename, "method" : stack.function, "line" : stack.lineno } @staticmethod def get_trace(exception:Optional[Exception] = None) -> list[str]: return trace_format_stack()[:-2] + ( extract_traceback(exception.__traceback__).format() if exception else [])