98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
import pathlib
|
|
import typing
|
|
|
|
import toml
|
|
from collections import UserDict
|
|
import atexit
|
|
|
|
from heurams.services.logger import get_logger
|
|
from heurams.services.exceptions import WTFException
|
|
|
|
# 我们的流程是: 找到文件名: 返回文件名里头的数据; 找不到: 继续查索引; 所以 self.data 除了存本级各种索引球用没得
|
|
logger = get_logger(__name__)
|
|
|
|
class ConfigDict(UserDict): # 舒服了
|
|
_instances = {} # 必须使用单例模式, 不然有严重的多实例导致的配置无法持久化问题
|
|
|
|
def __new__(cls, config_path: pathlib.Path, dict=None):
|
|
if dict:
|
|
raise WTFException("不要放默认值...")
|
|
|
|
# 规范化路径, 免得单例存在"别名"
|
|
path_key = config_path.resolve()
|
|
|
|
if path_key in cls._instances:
|
|
return cls._instances[path_key]
|
|
|
|
instance = super().__new__(cls)
|
|
cls._instances[path_key] = instance
|
|
return instance
|
|
|
|
def __init__(self, config_path: pathlib.Path, dict = None): # 需要自己把自己提起来
|
|
# 避免重复初始化
|
|
if hasattr(self, '_initialized'):
|
|
return
|
|
self._initialized = True
|
|
if dict:
|
|
raise WTFException("不要放默认值...")
|
|
super().__init__(dict)
|
|
logger = get_logger(__name__)
|
|
self.path = config_path
|
|
self.is_dir = self.path.is_dir()
|
|
if self.is_dir:
|
|
self.update_index()
|
|
else:
|
|
with open(self.path, 'r+') as f: #TODO: 给这个做缓存
|
|
try:
|
|
self.data = toml.load(f)
|
|
except:
|
|
self.data = {}
|
|
self.persist = lambda: False # 不修改错误的配置文件
|
|
|
|
def __getitem__(self, key):
|
|
# 我们实现了先进的懒狗加载
|
|
value = super().__getitem__(key)
|
|
if isinstance(value, pathlib.Path):
|
|
return ConfigDict(value)
|
|
return value
|
|
|
|
def __contains__(self, key):
|
|
return super().__contains__(key)
|
|
|
|
def __setitem__(self, key, value):
|
|
origvalue = super().__getitem__(key) # 所以你不该访问不存在的对象
|
|
if isinstance(origvalue, ConfigDict):
|
|
if origvalue.path.is_dir():
|
|
raise WTFException("你怎么能变更目录配置的内容呢?!")
|
|
else:
|
|
# 对文件, 我们允许这种覆写存在
|
|
# 但是不准变类型
|
|
origvalue.data = value
|
|
super().__setitem__(key, value)
|
|
|
|
def update_index(self): # 如果有人没事干在config里面创建指向config的符号链接 这玩意会崩溃 但是不要修复: 需要这个符号链接特性
|
|
for i in self.path.iterdir():
|
|
if i.name.startswith('_'):
|
|
if i.name == '_.toml' and not i.is_dir():
|
|
with open(self.path/'_.toml', 'r+') as f:
|
|
self.data.update(dict(toml.load(f)))
|
|
continue
|
|
if i.is_dir():
|
|
self.data[i.name] = i
|
|
else:
|
|
if i.suffix == '.toml':
|
|
self.data[i.stem] = i
|
|
else:
|
|
logger.debug(f"配置目录中有无效的文件 {i.stem}") # what's up bro
|
|
|
|
def persist(self):
|
|
if self.is_dir:
|
|
for i in self.data.keys():
|
|
j = self[i]
|
|
if isinstance(j, ConfigDict):
|
|
j.persist()
|
|
logger.debug("完成配置持久化")
|
|
return
|
|
|
|
with open(self.path, 'w+') as f:
|
|
toml.dump(self.data, f) |