File size: 8,077 Bytes
e79128b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | import json
from pathlib import Path
from threading import RLock # 改成 RLock
import os
import copy
class ConfigManager:
_instance = None
_lock = RLock() # 用 RLock 替换 Lock,允许在同一个线程中重复获取锁
@classmethod
def get_instance(cls):
"""获取单例实例"""
# 先判断是否存在实例,如果不存在再加锁进行初始化
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = cls()
return cls._instance
def __init__(self):
# 防止重复初始化
if hasattr(self, "_initialized") and self._initialized:
return
self._initialized = True
self._config_path = Path.home() / ".config" / "PDFMathTranslate" / "config.json"
self._config_data = {}
# 这里不要再加锁,因为外层可能已经加了锁 (get_instance), RLock也无妨
self._ensure_config_exists()
def _ensure_config_exists(self, isInit=True):
"""确保配置文件存在,如果不存在则创建默认配置"""
# 这里也不需要显式再次加锁,原因同上,方法体中再调用 _load_config(),
# 而 _load_config() 内部会加锁。因为 RLock 是可重入的,不会阻塞。
if not self._config_path.exists():
if isInit:
self._config_path.parent.mkdir(parents=True, exist_ok=True)
self._config_data = {} # 默认配置内容
self._save_config()
else:
raise ValueError(f"config file {self._config_path} not found!")
else:
self._load_config()
def _load_config(self):
"""从 config.json 中加载配置"""
with self._lock: # 加锁确保线程安全
with self._config_path.open("r", encoding="utf-8") as f:
self._config_data = json.load(f)
def _save_config(self):
"""保存配置到 config.json"""
with self._lock: # 加锁确保线程安全
# 移除循环引用并写入
cleaned_data = self._remove_circular_references(self._config_data)
with self._config_path.open("w", encoding="utf-8") as f:
json.dump(cleaned_data, f, indent=4, ensure_ascii=False)
def _remove_circular_references(self, obj, seen=None):
"""递归移除循环引用"""
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return None # 遇到已处理过的对象,视为循环引用
seen.add(obj_id)
if isinstance(obj, dict):
return {
k: self._remove_circular_references(v, seen) for k, v in obj.items()
}
elif isinstance(obj, list):
return [self._remove_circular_references(i, seen) for i in obj]
return obj
@classmethod
def custome_config(cls, file_path):
"""使用自定义路径加载配置文件"""
custom_path = Path(file_path)
if not custom_path.exists():
raise ValueError(f"Config file {custom_path} not found!")
# 加锁
with cls._lock:
instance = cls()
instance._config_path = custom_path
# 此处传 isInit=False,若不存在则报错;若存在则正常 _load_config()
instance._ensure_config_exists(isInit=False)
cls._instance = instance
@classmethod
def get(cls, key, default=None):
"""获取配置值"""
instance = cls.get_instance()
# 读取时,加锁或不加锁都行。但为了统一,我们在修改配置前后都要加锁。
# get 只要最终需要保存,则会加锁 -> _save_config()
if key in instance._config_data:
return instance._config_data[key]
# 若环境变量中存在该 key,则使用环境变量并写回 config
if key in os.environ:
value = os.environ[key]
instance._config_data[key] = value
instance._save_config()
return value
# 若 default 不为 None,则设置并保存
if default is not None:
instance._config_data[key] = default
instance._save_config()
return default
# 找不到则抛出异常
# raise KeyError(f"{key} is not found in config file or environment variables.")
return default
@classmethod
def set(cls, key, value):
"""设置配置值并保存"""
instance = cls.get_instance()
with instance._lock:
instance._config_data[key] = value
instance._save_config()
@classmethod
def get_translator_by_name(cls, name):
"""根据 name 获取对应的 translator 配置"""
instance = cls.get_instance()
translators = instance._config_data.get("translators", [])
for translator in translators:
if translator.get("name") == name:
return translator["envs"]
return None
@classmethod
def set_translator_by_name(cls, name, new_translator_envs):
"""根据 name 设置或更新 translator 配置"""
instance = cls.get_instance()
with instance._lock:
translators = instance._config_data.get("translators", [])
for translator in translators:
if translator.get("name") == name:
translator["envs"] = copy.deepcopy(new_translator_envs)
instance._save_config()
return
translators.append(
{"name": name, "envs": copy.deepcopy(new_translator_envs)}
)
instance._config_data["translators"] = translators
instance._save_config()
@classmethod
def get_env_by_translatername(cls, translater_name, name, default=None):
"""根据 name 获取对应的 translator 配置"""
instance = cls.get_instance()
translators = instance._config_data.get("translators", [])
for translator in translators:
if translator.get("name") == translater_name.name:
if translator["envs"][name]:
return translator["envs"][name]
else:
with instance._lock:
translator["envs"][name] = default
instance._save_config()
return default
with instance._lock:
translators = instance._config_data.get("translators", [])
for translator in translators:
if translator.get("name") == translater_name.name:
translator["envs"][name] = default
instance._save_config()
return default
translators.append(
{
"name": translater_name.name,
"envs": copy.deepcopy(translater_name.envs),
}
)
instance._config_data["translators"] = translators
instance._save_config()
return default
@classmethod
def delete(cls, key):
"""删除配置值并保存"""
instance = cls.get_instance()
with instance._lock:
if key in instance._config_data:
del instance._config_data[key]
instance._save_config()
@classmethod
def clear(cls):
"""删除配置值并保存"""
instance = cls.get_instance()
with instance._lock:
instance._config_data = {}
instance._save_config()
@classmethod
def all(cls):
"""返回所有配置项"""
instance = cls.get_instance()
# 这里只做读取操作,一般可不加锁。不过为了保险也可以加锁。
return instance._config_data
@classmethod
def remove(cls):
instance = cls.get_instance()
with instance._lock:
os.remove(instance._config_path)
|