219 lines
5.8 KiB
Python
219 lines
5.8 KiB
Python
#!/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
|