Difficult-Rocket/Difficult_Rocket/utils/tools.py
2023-12-03 16:54:07 +08:00

251 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
# All rights reserved
# -------------------------------
"""
writen by shenjackyuanjie
mail: 3695888@qq.com
github: @shenjackyuanjie
gitee: @shenjackyuanjie
"""
import os
import sys
import time
import math
import json
import rtoml
import logging
import configparser
from pathlib import Path
from typing import Union, Optional
from xml.etree import ElementTree
from defusedxml.ElementTree import parse
from Difficult_Rocket.exception.unsupport import NoMoreJson5
# logger
tools_logger = logging.getLogger("tools")
"""
file config
"""
file_error = {
FileNotFoundError: "no {filetype} file was founded!:\n file name: {filename}\n file_type: {filetype}\n stack: {stack}",
KeyError: "no stack in {filetype} file {filename} was found! \n file type: {} \n file name: {} \n stack: {stack}",
Exception: "get some {error_type} when read {filetype} file {filename}! \n file type: {} \n file name: {} \n stack: {stack}",
}
def load_file(
file_name: Union[str, Path],
stack: Optional[Union[str, list, dict]] = None,
raise_error: Optional[bool] = True,
encoding: Optional[str] = "utf-8",
) -> Union[dict, ElementTree.ElementTree]:
if isinstance(file_name, Path):
file_name = str(file_name)
f_type = file_name[file_name.rfind(".") + 1 :] # 从最后一个.到末尾 (截取文件格式)
get_file = NotImplementedError("解析失败,请检查文件类型/文件内容/文件是否存在!")
try:
if f_type == "xml":
xml_load: ElementTree.ElementTree = parse(file_name)
if stack is not None:
get_file = xml_load.findall(stack)
elif (f_type == "config") or (f_type == "conf") or (f_type == "ini"):
get_file = configparser.ConfigParser()
get_file.read(file_name)
if stack:
get_file = get_file[stack]
elif f_type == "toml":
with open(file_name, mode="r", encoding=encoding) as file:
get_file = rtoml.load(file)
if stack is not None:
get_file = get_file[stack]
elif f_type == "json":
with open(file_name, mode="r", encoding=encoding) as file:
get_file = json.load(file)
if stack is not None:
get_file = get_file[stack]
elif f_type == "json5":
raise NoMoreJson5("我说什么也不用json5了喵的")
except Exception as exp:
error_type = type(exp)
if error_type in file_error:
tools_logger.error(
file_error[error_type].format(
filetype=f_type, filename=file_name, stack=stack
)
)
else:
tools_logger.error(
file_error[Exception].format(
error_type=error_type,
filetype=f_type,
filename=file_name,
stack=stack,
)
)
if raise_error:
raise exp from None
return get_file
def save_dict_file(file_name: str, data: dict, encoding: str = "utf-8") -> bool:
f_type = file_name[file_name.rfind(".") + 1 :] # 从最后一个.到末尾 (截取文件格式)
try:
if (f_type == "config") or (f_type == "conf") or (f_type == "ini"):
return False
elif f_type == "toml":
with open(file_name, mode="w", encoding=encoding) as file:
rtoml.dump(data, file)
elif f_type == "json":
with open(file_name, mode="w", encoding=encoding) as file:
json.dump(data, file)
elif f_type == "json5":
raise NoMoreJson5("我说什么也不用json5了喵的")
except Exception as exp:
raise exp
# main config
main_config_file = load_file("./config/main.toml")
def get_At(name, in_xml, need_type=str):
"""
get Attribute from a XML tree
will raise TypeError if input is not str or list
XML json5 no! toml yes!
"""
name_type = type(name)
if name_type == list:
At_list = []
for need_name in name:
if in_xml.hasAttribute(need_name):
get = in_xml.getAttribute(need_name)
At_list.append(need_type(get))
else:
continue
return At_list
elif name_type == str:
if in_xml.hasAttribute(name):
attr = in_xml.getAttribute(name)
else:
return None
else:
raise TypeError(
"only str and list type is ok but you give me a" + name_type + "type"
)
return need_type(attr)
def default_name_handler(name_: str) -> str:
"""
won't change the string
just return one
"""
name = name_
name = name.replace("{time.time}", str(time.time()))
name = name.replace("{dir}", str(os.getcwd()))
name = name.replace("{py_v}", str(sys.version.split(" ")[0]))
name = name.replace("{version}", str(main_config_file["runtime"]["version"]))
name = name.replace("{write_v}", str(main_config_file["runtime"]["write_py_v"]))
return name
def name_handler(name: str, formats: dict = None) -> str:
if formats is None:
return default_name_handler(name)
name = default_name_handler(name)
for need_replace in formats:
replace = formats[need_replace]
if need_replace == "{date}":
if "{date}" in formats:
replace = time.strftime(formats["{date}"], time.gmtime(time.time()))
else:
replace = time.strftime(
main_config_file["runtime"]["date_fmt"], time.gmtime(time.time())
)
name = name.replace(need_replace, replace)
return name
"""
some tools
"""
yes = ["true", "1", 1, 1.0, True]
no = ["false", "0", 0, 0.0, False, None]
def format_bool(thing) -> bool:
"""
:param thing 啥都行,只要是能传进来的都可以
如果看起来像"True" 比如 'true','1',1
就返回 True
如果看起来像"False" 比如 'false','0',0
就返回 False
都不像就 raise TypeError
感谢来自MCDReforged的各位同志《你能在MCDR群里聊除了MCDR的任何东西》
"""
if (thing in yes) or (isinstance(thing, str) and thing.lower() in yes):
return True
elif (thing in no) or (isinstance(thing, str) and thing.lower() in no):
return False
else:
raise TypeError("Need a 'like bool' not a {}".format(thing))
level_ = [
"DEBUG",
"INFO",
"WARNING",
"ERROR",
"CRITICAL",
logging.DEBUG,
logging.INFO,
logging.WARNING,
logging.ERROR,
logging.CRITICAL,
]
def log_level(level):
if level in level_:
if (level == "DEBUG") or (level == logging.DEBUG):
return logging.DEBUG
if (level == "INFO") or (level == logging.INFO):
return logging.INFO
if (level == "WARNING") or (level == logging.WARNING):
return logging.WARNING
if (level == "ERROR") or (level == logging.ERROR):
return logging.ERROR
if (level == "CRITICAL") or (level == logging.CRITICAL):
return logging.CRITICAL
else:
raise ValueError("Need a like logging.level thing not anything else")
# linear_algebra
def C_R_P(position, degrees): # stand for calculation
"""
very thanks for lenny from pyglet developer
https://github.com/LennyPhoenix
this part of code is write by him
"""
radians = degrees * (math.pi / 180)
cos = math.cos(radians)
sin = math.sin(radians)
rotated_pos = (
position[0] * cos - position[1] * sin,
position[0] * sin + position[1] * cos,
)
return rotated_pos