#!/usr/bin/env python3 # -*- coding: utf-8 -*- from Utils.Check import Check from typing import Any, Self, Optional from Utils.Patterns import RE from re import Match as REMatch from inspect import stack as get_stack from inspect import FrameInfo from json import loads as json_decode from json import dumps as json_encode from random import random as math_random from base64 import b64decode as base64_decode from base64 import b64encode as base64_encode class Utils: @classmethod def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]: return [key for item in items for key in ( (item,) if Check.is_key(item) else cls.get_keys(*item) if Check.is_array(item) else tuple())] @classmethod def get_dictionaries(cls:type[Self], *items:list[Any|None]) -> list[dict[str, Any|None]]: return [dictionary for item in items for dictionary in ( (item,) if Check.is_dictionary(item) else cls.get_dictionaries(*item) if Check.is_array(item) else tuple())] @classmethod def get_dictionary(cls:type[Self], *items:list[Any|None]) -> dict[str, Any|None]: return {key : value for item in items for key, value in ( item if Check.is_dictionary(item) else cls.get_dictionary(*item) if Check.is_array(item) else {}).items()} @classmethod def get_values(cls:type[Self], keys:str|list|tuple, inputs:dict[str, Any|None]|list[Any|None]|tuple[Any|None], _default:Optional[Any] = None) -> Any|None: keys = cls.get_keys(keys) if len(keys): subinputs:dict[str, Any|None] for subinputs in cls.get_dictionaries(inputs): key:str for key in keys: if key in subinputs: return subinputs[key] return _default @classmethod def string_variables(cls:type[Self], string:str, variables:dict[str, Any|None], _default:Optional[str] = None) -> str|None: variables = cls.get_dictionary(variables) def callback(matches:REMatch) -> str: key:str = matches.group(1) return ( str(variables[key]) if key in variables else str(_default) if _default is not None else matches.group(0)) return RE.STRING_VARIABLES.sub(callback, str(string)) @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 } @classmethod def get_strings(cls:type[Self], *items:list[Any|None]) -> list[str]: return [string for item in items for string in ( cls.get_strings(*item) if Check.is_array(item) else (item,) ) if Check.is_string(string)] @staticmethod def json_encode(data:dict[str, Any|None]|list[Any|None]|tuple[Any|None]) -> str|None: try: return json_encode(data, ensure_ascii=False) except Exception as exception: pass return None @staticmethod def json_decode(data:str) -> dict[str, Any|None]|list[Any|None]|tuple[Any|None]|None: try: return json_decode(data) except Exception as exception: 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 recursive(data:str, callback:Any) -> str: cache:str = data while True: data = cache cache = callback(cache) if data == cache: break return cache @staticmethod def get_array(item:Any|None) -> list[Any|None]: return item if Check.is_array(item) else [item] @staticmethod def get_random(_from:Optional[int] = None, _to:Optional[int] = None) -> Any|None: return ( math_random() if _from is None else _from[int(math_random() * len(_from))] if Check.is_array(_from) else ( int(math_random() * _from) if _to is None else int(math_random() * (_to - _from) + _from) if Check.is_integer(_to) else math_random() * (_to - float(_from)) + float(_from) if Check.is_float(_to) else None) if Check.is_integer(_from) else ( math_random() * _from if _to is None else math_random() * (float(_to) - _from) + _from if Check.is_number(_to) else None) if Check.is_float(_from) else None) @classmethod def randomize_array(cls:type[Self], items:list[Any|None]) -> None: l:int = len(items) for i in range(l): j:int = cls.get_random(l) if i != j: items[i], items[j] = items[j], items[i] @staticmethod def base64_encode(data:str|bytes, encoding:str = "utf-8") -> str|None: try: return base64_encode(data.encode(encoding) if Check.is_string(data) else data).decode(encoding) except Exception as exception: pass return None @staticmethod def base64_decode(data:str|bytes, encoding:str = "utf-8", is_string:bool = True) -> str|bytes|None: try: decoded:bytes = base64_decode(data.encode(encoding) if Check.is_string(data) else data) return decoded.decode(encoding) if is_string else decoded except Exception as exception: pass return None