#!/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