LibreTranslatePlus/Python/Application/LibreTranslatePlus.py

418 lines
15 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from re import compile as re_compile
from os.path import dirname as directory_name
from os.path import abspath as path_absolute
from os.path import exists as path_exists
from os import mkdir as make_directory
from json import loads as json_decode
from inspect import stack as get_stack
from datetime import datetime
from traceback import format_stack as trace_format_stack
from traceback import extract_tb as extract_traceback
if "ltp_basic_texts" not in globals():
ltp_basic_texts = {}
class LibreTranslatePlus:
re_path_slashes = re_compile(r'[\\\\\/]+')
re_root_fix = re_compile(r'[\\\\\/][^\\\\\/]+[\\\\\/]?$')
re_string_variables = re_compile(r'\{([^\{\}]+)\}')
re_break_lines = re_compile(r'\r\n|[\r\n]')
re_exception_line = re_compile(r'^\s*File "([^"]+)", line ([0-9]+), in (.+)$')
def __init__(self, inputs = None):
self.__print_types = {}
self.__print_format = "[{type}] {yyyy}{mm}{dd} {hh}{ii}{ss} {line}-{file}({method}): {message}"
self.__default_texts = None
texts = globals()["ltp_basic_texts"] if "ltp_basic_texts" in globals() else None
if not isinstance(inputs, dict):
inputs = {}
inputs["default_texts"] = texts
if isinstance(texts, dict):
keys = tuple(texts.keys())
if len(keys) and isinstance(texts[keys[0]], dict):
self.__default_texts = texts[keys[0]]
self._print("info", "ltp_welcome")
self._print("info", "ltp_building")
self.__started = False
self.__closed = False
self.__root_paths = ["", LibreTranslatePlus.re_root_fix.sub(r'', directory_name(path_absolute(__file__)))]
self.__is_dos = "\\" in self.__root_paths[1]
self.__path_slash = "\\" if self.__is_dos else "/"
if self.__root_paths[1][-1] != self.__path_slash:
self.__root_paths += self.__path_slash
self.__root_paths[1] = self.fix_path(self.__root_paths[1])
self.settings = LibreTranslatePlus.Settings(self, inputs) if hasattr(LibreTranslatePlus, "Settings") else None
self.__print_format = self.settings.get("print_format") or self.__print_format
self.i18n = LibreTranslatePlus.I18N(self, inputs) if hasattr(LibreTranslatePlus, "I18N") else None
self.threads = LibreTranslatePlus.Threads(self, inputs) if hasattr(LibreTranslatePlus, "Threads") else None
self.terminal = LibreTranslatePlus.Terminal(self, inputs) if hasattr(LibreTranslatePlus, "Terminal") else None
self.connections = LibreTranslatePlus.Connections(self, inputs) if hasattr(LibreTranslatePlus, "Connections") else None
self.models = LibreTranslatePlus.Models(self, inputs) if hasattr(LibreTranslatePlus, "Models") else None
self.__set_common_variables()
self._print("ok", "ltp_built")
self.settings.get("autostart") and self.start()
def start(self, callback = None):
self._print("info", "ltp_starting")
end = lambda status:callable(callback) and callback(status)
if self.__started:
self._print("warn", "ltp_already_started")
end(False)
return False
self.__started = True
if hasattr(self, "settings"):
self.settings.start()
self.__print_format = self.settings.get("print_format") or self.__print_format
self.__set_common_variables()
for key in ("i18n", "threads", "terminal", "connections", "models"):
if hasattr(self, key):
submodule = getattr(self, key)
if hasattr(submodule, "start"):
substart = getattr(submodule, "start")
callable(substart) and substart()
self._print("ok", "ltp_started")
end(True)
return True
def __set_common_variables(self):
self.__show_save_file_exception = self.settings.get(("show_save_file_exception_message", "show_exception_message"))
self.__show_save_file_error = self.settings.get(("show_save_file_error_message", "show_error_message"))
self.__show_save_file_ok = self.settings.get(("show_save_file_ok_message", "show_ok_message"))
def close(self, callback = None, *_):
self._print("info", "ltp_closing")
end = lambda status:callable(callback) and callback(status)
if self.__closed:
self._print("warn", "ltp_already_closed")
end(False)
return False
self.__closed = True
hasattr(self, "threads") and self.threads.close()
self._print("ok", "ltp_closed")
self._print("info", "ltp_bye")
end(True)
return True
def fix_path(self, path):
return LibreTranslatePlus.re_path_slashes.sub(self.__path_slash, path)
def load_file(self, path, show_exception = None):
error = (
1 << 1 if path == None else
1 << 2 if not isinstance(path, str) else
1 << 3 if not path else
0)
results = None
if not error:
for root in self.__root_paths:
full_path = self.fix_path((root or "") + ("/" if root else "") + path)
if path_exists(full_path):
try:
with open(full_path, "r") as opened:
results = opened.read()
except:
error |= 1 << 0
break
if results == None:
error |= 1 << 4
if (
show_exception if isinstance(show_exception, bool) else
self.settings.get(("load_file_show_exception", "show_exception")) if hasattr(self, "settings") else
False
):
pass
return (results, error)
def json_decode(self, inputs, show_exception = None):
error = (
1 << 1 if inputs == None else
1 << 2 if not isinstance(inputs, str) else
1 << 3 if not inputs else
0)
results = None
if not error:
inputs = inputs.strip()
error |= (
1 << 4 if not inputs else
1 << 5 if inputs[0] not in ('{', '[') else
1 << 6 if inputs[-1] not in ('}', ']') else
0)
if not error:
try:
results = json_decode(inputs)
except:
error |= 1 << 0
if (
show_exception if isinstance(show_exception, bool) else
self.settings.get(("json_decode_show_exception", "show_exception")) if hasattr(self, "settings") else
False
):
pass
return (results, error)
def load_json(self, inputs, show_exception = None):
if isinstance(inputs, str) and inputs and inputs[0] not in ('[', '{'):
(inputs, error) = self.load_file(inputs, show_exception)
(results, suberror) = self.json_decode(inputs, False)
return (results, error | (suberror << 5))
@staticmethod
def string_variables(string, variables = None, default = None):
variables = [_set for _set in (variables if isinstance(variables, (list, tuple)) else [variables]) if isinstance(variables, dict)]
def callback(matches):
key = matches.group(1)
for _set in variables:
if key in _set:
return str(_set[key])
return matches.group(0)
return LibreTranslatePlus.re_string_variables.sub(callback, str(string))
def add_print_types(self, inputs):
if isinstance(inputs, (list, tuple)):
for subinputs in inputs:
self.add_print_types(inputs)
elif isinstance(inputs, dict):
for key, alternatives in inputs.items():
main_key = None
for subkey in [key] + (
[alternatives] if isinstance(alternatives, str) else
alternatives if isinstance(alternatives, (list, tuple)) else
[]):
subkey = subkey.lower()
if subkey in self.__print_types:
if main_key == None:
main_key = subkey
continue
done = False
for print_key, subalternatives in self.__print_types.items():
if subkey in subalternatives:
done = True
if main_key == None:
main_key = print_key
break
if done:
continue
if main_key:
self.__print_types[main_key] += [subkey]
else:
self.__print_types[main_key] = []
main_key = subkey
elif isinstance(inputs, str):
self.add_print_types(self.load_json(inputs))
def _get_print_type(self, key):
if not isinstance(key, str) or not key:
return self.settings.get(("default_print_type", "print_type"))
for subkey, alternatives in self.__print_types.items():
if subkey == key or key in alternatives:
return subkey
return key
def get_action_data(self, i = 1):
stack = get_stack()[1 + i]
return {
"file" : stack.filename,
"method" : stack.function,
"line" : stack.lineno
}
def _print(self, _type, message, variables = None, level = 0, default = None):
date = datetime.now()
text = None
_type = self._get_print_type(_type).upper()[0:4]
l = len(_type)
text = self.i18n.get(message, variables, default) if hasattr(self, "i18n") else LibreTranslatePlus.string_variables(
(
"".join(self.__default_texts[message]) if isinstance(self.__default_texts[message], (list, tuple)) else
self.__default_texts[message]
) if message in self.__default_texts else
("".join(default) if isinstance(default, (list, tuple)) else default) if default != None else message,
variables
)
own = {
"yyyy" : date.year,
"year" : date.year,
"y" : date.year % 100,
"message" : text,
"i18n" : message,
**self.get_action_data(1 + level),
**(variables if isinstance(variables, dict) else {}),
"type" : " "[l and int((l - 1) / 2) + 1 or 0:] + _type + " "[int(l / 2):]
}
own["yy"] = ("00" + str(own["y"]))[-2:]
for key in ("month", "day", "hour", "minute", "second"):
own[key[0] if key != "minute" else "i"] = own[key] = getattr(date, key)
for key in "ymdhis":
own[key + key] = ("00" + str(own[key]))[-2:]
print(LibreTranslatePlus.string_variables(self.__print_format, own))
def validate(self, error, messages = tuple(), variables = None, error_message = None, ok_message = None):
if error:
if isinstance(error_message, str) and error_message:
if isinstance(messages, str):
messages = (messages,)
_list = ""
i = 0
l = len(messages) if isinstance(messages, (list, tuple)) else 0
while 1 << i <= error:
if (1 << i) & error:
_list += "\n " + self.i18n.get(messages[i] if i < l and messages[i] else "error_message_" + str(i), variables)
i += 1
self._print("error", error_message, {**variables, "code" : error, "list" : _list}, 1)
return False
isinstance(ok_message, str) and ok_message and self._print("ok", ok_message, variables, 1)
return True
def exception(self, exception, message = None, variables = None):
lines = extract_traceback(exception.__traceback__).format()
line_matches = LibreTranslatePlus.re_exception_line.match(lines[-1])
data = {
**(variables if isinstance(variables, dict) else {}),
**self.get_action_data(1),
"lines" : "",
"exception_message" : str(exception),
"method" : line_matches.group(3),
"line" : line_matches.group(2),
"file" : line_matches.group(1)
}
for block in trace_format_stack()[:-2] + lines:
if block:
data["lines"] += "\n " + LibreTranslatePlus.re_break_lines.split(block.strip())[0]
message and self._print("exception", message, data)
def save_file(self, path, data):
error = (
((
1 << 0 if path == None else
1 << 1 if not isinstance(path, str) else
1 << 2 if not path else
0) << 0) |
((
1 << 0 if data == None else
1 << 1 if not isinstance(data, str) else
0) << 3) |
0) << 1
try:
directory = path[:2] if self.__is_dos else ""
for directory_name in LibreTranslatePlus.re_path_slashes.split(path[2 if self.__is_dos else 1:])[:-1]:
directory += self.__path_slash + directory_name
if not path_exists(directory):
make_directory(directory)
with open(self.fix_path(path), 'w') as opened:
opened.write(data)
except Exception as exception:
error |= 1 << 0
self.exception(exception, self.__show_save_file_exception and "ltp_save_file_exception", {
"path" : path,
"length" : len(data) if isinstance(data, str) else None
})
self.validate(
error,
(
"path_null",
"path_not_string",
"path_empty",
"data_null",
"data_not_string"
),
{
"path" : path,
"length" : len(data) if isinstance(data, str) else None
},
self.__show_save_file_error and "ltp_save_file_error",
self.__show_save_file_ok and "ltp_save_file_ok"
)
return error
def get_root_directory(self):
return "" + self.__root_paths[1]
@staticmethod
def path_exists(path):
return path_exists(path)
def _echo(self, attributes, parameters):
print(parameters[0] if len(parameters) else "")