327 lines
9.4 KiB
Python
327 lines
9.4 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from typing import Any, Optional, Sequence, Self
|
|
from re import Match as REMatch
|
|
from os.path import abspath as absolute_path, dirname as directory_name, exists as path_exists, isfile as is_file
|
|
from json import loads as json_decode
|
|
from io import FileIO
|
|
from mimetypes import guess_type as get_mime_by_extension
|
|
from inspect import FrameInfo, stack as get_stack
|
|
from json import dumps as json_encode, loads as json_decode
|
|
from base64 import b64encode as base64_encode, b64decode as base64_decode
|
|
from Utils.Checks import Check
|
|
from Utils.Patterns import RE
|
|
|
|
ROOT_PATH:str = absolute_path(directory_name(__file__))
|
|
SLASH:str = "/" if "/" in ROOT_PATH else "\\"
|
|
|
|
class Common:
|
|
|
|
ROOT_PATH:str = ROOT_PATH
|
|
SLASH:str = SLASH
|
|
ROOTS_PATH:list[str] = [""] + [ROOT_PATH + (SLASH + "..") * i for i in range(4)]
|
|
SPECIAL_REGULAR_EXPRESSION_CHARACTERS:dict[str, str] = {
|
|
"\r" : "r",
|
|
"\n" : "n",
|
|
"\t" : "t"
|
|
}
|
|
|
|
@classmethod
|
|
def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]:
|
|
|
|
keys:list[str] = []
|
|
item:Any|None
|
|
|
|
for item in items:
|
|
if Check.is_key(item):
|
|
item not in keys and keys.append(item)
|
|
elif Check.is_array(item):
|
|
|
|
key:str
|
|
|
|
for key in cls.get_keys(*item):
|
|
key not in keys and keys.append(key)
|
|
|
|
return keys
|
|
|
|
@classmethod
|
|
def get_texts(cls:type[Self], *items:list[Any|None]) -> list[str]:
|
|
|
|
texts:list[str] = []
|
|
item:Any|None
|
|
|
|
for item in items:
|
|
if Check.is_string(item):
|
|
texts.append(item)
|
|
elif Check.is_array(item):
|
|
|
|
text:str
|
|
|
|
for text in cls.get_texts(*item):
|
|
texts.append(text)
|
|
|
|
return texts
|
|
|
|
@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 Check.is_dictionary(item):
|
|
dictionaries.append(item)
|
|
elif Check.is_array(item):
|
|
|
|
dictionary:dict[str, Any|None]
|
|
|
|
for dictionary in cls.get_dictionaries(*item):
|
|
dictionaries.append(dictionary)
|
|
|
|
return dictionaries
|
|
|
|
@classmethod
|
|
def get_dictionary(cls:type[Self], inputs:Any|None, overwrite:bool = False) -> dict[str, Any|None]:
|
|
|
|
dictionary:dict[str, Any|None] = {}
|
|
|
|
if Check.is_dictionary(inputs):
|
|
|
|
key:str
|
|
value:Any|None
|
|
|
|
for key, value in inputs.items():
|
|
if overwrite or key not in dictionary:
|
|
dictionary[key] = value
|
|
|
|
elif Check.is_array(inputs):
|
|
|
|
subinputs:dict[str, Any|None]
|
|
|
|
for subinputs in inputs:
|
|
|
|
key:str
|
|
value:Any|None
|
|
|
|
for key, value in cls.get_dictionaries(subinputs).items():
|
|
if overwrite or key not in dictionary:
|
|
dictionary[key] = value
|
|
|
|
return dictionary
|
|
|
|
@classmethod
|
|
def get_value(cls:type[Self],
|
|
keys:str|Sequence[str],
|
|
inputs:dict[str, Any|None]|Sequence[Any|None],
|
|
default:Optional[Any|None] = None
|
|
) -> Any|None:
|
|
if len(keys := cls.get_keys(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,
|
|
inputs:dict[str, Any|None]|Sequence[Any|None],
|
|
default:Optional[str] = None
|
|
) -> str:
|
|
|
|
inputs = cls.get_dictionary(inputs)
|
|
|
|
def replace(matches:REMatch) -> str:
|
|
|
|
key:str = matches.group(1)
|
|
|
|
return str(
|
|
inputs[key] if key in inputs else
|
|
default if default is not None else
|
|
matches.group(0))
|
|
|
|
return RE.STRING_VARIABLES.sub(replace, str(string))
|
|
|
|
@classmethod
|
|
def fix_path(cls:type[Self], path:str) -> str:
|
|
return RE.SLASHES.sub(cls.SLASH, path)
|
|
|
|
@classmethod
|
|
def get_absolute_path(cls:type[Self], path:str) -> str|None:
|
|
|
|
root:str
|
|
|
|
for root in cls.ROOTS_PATH:
|
|
|
|
absolute_path:str = cls.fix_path((root + cls.SLASH if root else "") + path)
|
|
|
|
if absolute_path[0] == "\\":
|
|
absolute_path = "\\" + absolute_path
|
|
|
|
if path_exists(absolute_path):
|
|
return absolute_path
|
|
return None
|
|
|
|
@classmethod
|
|
def load_file(cls:type[Self], path:str, mode:str = "r") -> str|bytes|None:
|
|
try:
|
|
|
|
file:FileIO
|
|
|
|
if mode == "r":
|
|
for format in (
|
|
"utf8", "cp1252", "ascii", "cp850", "latin1", "iso8859_1",
|
|
"utf16", "utf16le", "utf16be", "utf8sig", "iso8859_15"
|
|
):
|
|
try:
|
|
with open(cls.get_absolute_path(path), mode, encoding = format) as file:
|
|
return file.read()
|
|
except Exception as exception:
|
|
pass
|
|
elif mode == "rb":
|
|
with open(cls.get_absolute_path(path), mode) as file:
|
|
return file.read()
|
|
except Exception as exception:
|
|
pass
|
|
return None
|
|
|
|
@classmethod
|
|
def load_json(cls:type[Self],
|
|
data:str|dict[str, Any|None]|Sequence[Any|None],
|
|
only_dictionaries:bool = True
|
|
) -> list[dict[str, Any|None]|Sequence[Any|None]]:
|
|
|
|
json:list[dict[str, Any|None]|Sequence[Any|None]] = []
|
|
|
|
if isinstance(data, dict):
|
|
json.append(data)
|
|
elif isinstance(data, str):
|
|
|
|
subdata:dict[str, Any|None]|Sequence[Any|None]|None
|
|
|
|
try:
|
|
if subdata := json_decode(data):
|
|
json.extend(cls.load_json(subdata, only_dictionaries))
|
|
return
|
|
except Exception as exception:
|
|
pass
|
|
|
|
try:
|
|
json.extend(cls.load_json(json_decode(cls.load_file(data, "r")), only_dictionaries))
|
|
except Exception as exception:
|
|
pass
|
|
|
|
elif isinstance(data, (list, tuple)):
|
|
if only_dictionaries:
|
|
|
|
item:Any|None
|
|
|
|
for item in data:
|
|
json.extend(cls.load_json(item, only_dictionaries))
|
|
else:
|
|
json.append(data)
|
|
|
|
return json
|
|
|
|
@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 get_mime_from_path(path:str) -> str|None:
|
|
return get_mime_by_extension(path)[0]
|
|
|
|
@classmethod
|
|
def is_mark_key(cls:type[Self], key:str, marks:str|Sequence[str] = "AnP") -> bool:
|
|
|
|
mark:str
|
|
|
|
for mark in cls.get_keys(marks):
|
|
if key.startswith(mark + "_") and (
|
|
key.endswith("_start") or
|
|
key.endswith("_end")
|
|
):
|
|
return True
|
|
return False
|
|
|
|
@staticmethod
|
|
def is_file(path:str) -> bool:
|
|
return is_file(path)
|
|
|
|
@staticmethod
|
|
def json_encode(data:dict[str, Any|None]|Sequence[Any|None]) -> str|None:
|
|
try:
|
|
return json_encode(data)
|
|
except Exception as exception:
|
|
pass
|
|
return None
|
|
|
|
@staticmethod
|
|
def json_decode(data:str) -> dict[str, Any|None]|Sequence[Any|None]|None:
|
|
try:
|
|
return json_decode(data)
|
|
except Exception as exception:
|
|
pass
|
|
return None
|
|
|
|
@staticmethod
|
|
def base64_encode(data:bytes) -> str|None:
|
|
try:
|
|
return base64_encode(data).decode("utf-8")
|
|
except Exception as exception:
|
|
pass
|
|
return None
|
|
|
|
@staticmethod
|
|
def base64_decode(data:str) -> bytes|None:
|
|
try:
|
|
return base64_decode(data.encode("utf-8"))
|
|
except Exception as exception:
|
|
pass
|
|
return None
|
|
|
|
@classmethod
|
|
def data_encode(cls:type[Self], data:Any|None) -> str|None:
|
|
if Check.is_json_data(data):
|
|
data = cls.json_encode(data)
|
|
elif not Check.is_string(data):
|
|
data = str(data)
|
|
return cls.base64_encode(data.encode("utf-8"))
|
|
|
|
@classmethod
|
|
def data_decode(cls:type[Self], data:str) -> Any|None:
|
|
if Check.is_string(data):
|
|
data = cls.base64_decode(data)
|
|
if Check.is_string(data):
|
|
try:
|
|
return cls.json_decode(data)
|
|
except Exception as exception:
|
|
return data
|
|
return None |