tretool 0.2.1__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tretool-0.2.1 → tretool-0.3.0}/PKG-INFO +8 -4
- {tretool-0.2.1 → tretool-0.3.0}/README.md +1 -1
- {tretool-0.2.1 → tretool-0.3.0}/pyproject.toml +19 -3
- tretool-0.3.0/src/tretool/__init__.py +52 -0
- tretool-0.3.0/src/tretool/config.py +498 -0
- tretool-0.3.0/src/tretool/decoratorlib.py +423 -0
- tretool-0.3.0/src/tretool/encoding.py +421 -0
- tretool-0.3.0/src/tretool/httplib.py +730 -0
- tretool-0.3.0/src/tretool/jsonlib.py +767 -0
- tretool-0.3.0/src/tretool/logger.py +712 -0
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool/mathlib.py +0 -33
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool/path.py +19 -0
- tretool-0.3.0/src/tretool/platformlib.py +487 -0
- tretool-0.3.0/src/tretool/plugin.py +548 -0
- tretool-0.3.0/src/tretool/smartCache.py +569 -0
- tretool-0.3.0/src/tretool/tasklib.py +730 -0
- tretool-0.3.0/src/tretool/transform/pdf.py +574 -0
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool.egg-info/PKG-INFO +8 -4
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool.egg-info/SOURCES.txt +6 -3
- tretool-0.3.0/src/tretool.egg-info/requires.txt +4 -0
- tretool-0.2.1/src/tretool/__init__.py +0 -27
- tretool-0.2.1/src/tretool/config.py +0 -262
- tretool-0.2.1/src/tretool/encoding.py +0 -92
- tretool-0.2.1/src/tretool/jsonlib.py +0 -299
- tretool-0.2.1/src/tretool/markfunc.py +0 -152
- tretool-0.2.1/src/tretool/memorizeTools.py +0 -24
- tretool-0.2.1/src/tretool/platformlib.py +0 -332
- tretool-0.2.1/src/tretool/plugin.py +0 -348
- tretool-0.2.1/src/tretool/transform/pdf.py +0 -396
- tretool-0.2.1/src/tretool/writeLog.py +0 -69
- {tretool-0.2.1 → tretool-0.3.0}/LICENSE +0 -0
- {tretool-0.2.1 → tretool-0.3.0}/setup.cfg +0 -0
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool/plugin/plu.py +0 -0
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool/timelib.py +0 -0
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool/transform/__init__.py +0 -0
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool.egg-info/dependency_links.txt +0 -0
- {tretool-0.2.1 → tretool-0.3.0}/src/tretool.egg-info/top_level.txt +0 -0
- {tretool-0.2.1 → tretool-0.3.0}/tests/tests.py +0 -0
@@ -1,23 +1,27 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: tretool
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.0
|
4
4
|
Summary: 一个有着许多功能的Python工具库
|
5
5
|
Author-email: Jemy <sh_ljr_2013@163.com>
|
6
6
|
License-Expression: MIT
|
7
|
-
Project-URL: Homepage, https://github.com/
|
8
|
-
Project-URL: Issues, https://github.com/
|
7
|
+
Project-URL: Homepage, https://github.com/jemy/sampleproject
|
8
|
+
Project-URL: Issues, https://github.com/jemy/sampleproject/issues
|
9
|
+
Keywords: tool,tools,utility,utilities,productivity
|
9
10
|
Classifier: Programming Language :: Python :: 3
|
10
11
|
Classifier: Operating System :: OS Independent
|
11
12
|
Requires-Python: >=3.10
|
12
13
|
Description-Content-Type: text/markdown
|
13
14
|
License-File: LICENSE
|
15
|
+
Provides-Extra: dev
|
16
|
+
Requires-Dist: pytest; extra == "dev"
|
17
|
+
Requires-Dist: pipreqs; extra == "dev"
|
14
18
|
Dynamic: license-file
|
15
19
|
|
16
20
|
# tretool
|
17
21
|
|
18
22
|
## tretool - Python多功能工具库
|
19
23
|
|
20
|
-
[](https://www.python.org/)
|
21
25
|
|
22
26
|
**tretool** 是一个集成常用功能的Python工具库。
|
23
27
|
|
@@ -2,20 +2,28 @@
|
|
2
2
|
requires = [
|
3
3
|
"chardet",
|
4
4
|
"packaging",
|
5
|
+
"pdfminer",
|
6
|
+
"pdf2image",
|
7
|
+
"pillow",
|
8
|
+
"PyPDF2",
|
9
|
+
"psutil",
|
5
10
|
"rich",
|
11
|
+
"tabula",
|
12
|
+
|
6
13
|
"setuptools >= 77.0.3"
|
7
14
|
]
|
8
15
|
build-backend = "setuptools.build_meta"
|
9
16
|
|
10
17
|
[project]
|
11
18
|
name = "tretool"
|
12
|
-
version = "0.
|
19
|
+
version = "0.3.0"
|
13
20
|
authors = [
|
14
21
|
{ name="Jemy", email="sh_ljr_2013@163.com" },
|
15
22
|
]
|
16
23
|
description = "一个有着许多功能的Python工具库"
|
17
24
|
readme = "README.md"
|
18
25
|
requires-python = ">=3.10"
|
26
|
+
keywords = ["tool", "tools", "utility", "utilities", "productivity"]
|
19
27
|
classifiers = [
|
20
28
|
"Programming Language :: Python :: 3",
|
21
29
|
"Operating System :: OS Independent",
|
@@ -23,6 +31,14 @@ classifiers = [
|
|
23
31
|
license = "MIT"
|
24
32
|
license-files = ["LICEN[CS]E*"]
|
25
33
|
|
34
|
+
[project.optional-dependencies]
|
35
|
+
dev = [
|
36
|
+
"pytest",
|
37
|
+
"pipreqs"
|
38
|
+
]
|
39
|
+
|
26
40
|
[project.urls]
|
27
|
-
Homepage = "https://github.com/
|
28
|
-
Issues = "https://github.com/
|
41
|
+
Homepage = "https://github.com/jemy/sampleproject"
|
42
|
+
Issues = "https://github.com/jemy/sampleproject/issues"
|
43
|
+
|
44
|
+
[project.scripts]
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"""
|
2
|
+
# tretool
|
3
|
+
|
4
|
+
## tretool - Python多功能工具库
|
5
|
+
|
6
|
+
[](https://www.python.org/)
|
7
|
+
|
8
|
+
**tretool** 是一个集成常用功能的Python工具库。
|
9
|
+
"""
|
10
|
+
|
11
|
+
import sys
|
12
|
+
|
13
|
+
MIN_PY_VERSION = (3, 10)
|
14
|
+
|
15
|
+
if sys.version_info >= MIN_PY_VERSION:
|
16
|
+
from . import config
|
17
|
+
|
18
|
+
from . import decoratorlib
|
19
|
+
|
20
|
+
from . import encoding
|
21
|
+
|
22
|
+
from . import httplib
|
23
|
+
|
24
|
+
from . import jsonlib
|
25
|
+
|
26
|
+
from . import logger
|
27
|
+
|
28
|
+
from . import path
|
29
|
+
from . import platformlib
|
30
|
+
from . import plugin
|
31
|
+
|
32
|
+
from . import smartCache
|
33
|
+
|
34
|
+
from . import timelib
|
35
|
+
from . import transform
|
36
|
+
|
37
|
+
else:
|
38
|
+
current_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
39
|
+
required_version = f"{MIN_PY_VERSION[0]}.{MIN_PY_VERSION[1]}"
|
40
|
+
|
41
|
+
raise RuntimeError(
|
42
|
+
f"\n\n"
|
43
|
+
f"不兼容的Python版本\n\n"
|
44
|
+
f"Tretool需要Python {required_version}+ (检测到: Python {current_version})\n"
|
45
|
+
f"请执行以下操作之一:\n"
|
46
|
+
f"1. 升级Python到{required_version}或更高版本\n"
|
47
|
+
f"2. 使用兼容的Tretool版本\n\n"
|
48
|
+
f"升级Python推荐方法:\n"
|
49
|
+
f"- 使用pyenv: `pyenv install 3.10.x`\n"
|
50
|
+
f"- 从官网下载: https://www.python.org/downloads/\n"
|
51
|
+
f"- 使用conda: `conda install python=3.10`"
|
52
|
+
)
|
@@ -0,0 +1,498 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import threading
|
4
|
+
from typing import Any, Callable, Dict, Optional, Union, List, TypeVar, Generic
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from enum import Enum, auto
|
7
|
+
from pathlib import Path
|
8
|
+
import logging
|
9
|
+
from copy import deepcopy
|
10
|
+
|
11
|
+
# 配置日志
|
12
|
+
logging.basicConfig(level=logging.INFO)
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
T = TypeVar('T')
|
16
|
+
|
17
|
+
class ConfigError(Exception):
|
18
|
+
"""配置基础异常"""
|
19
|
+
pass
|
20
|
+
|
21
|
+
class ConfigLockedError(ConfigError):
|
22
|
+
"""配置被锁定异常"""
|
23
|
+
pass
|
24
|
+
|
25
|
+
class ConfigValidationError(ConfigError):
|
26
|
+
"""配置验证失败异常"""
|
27
|
+
pass
|
28
|
+
|
29
|
+
class ConfigOperation(Enum):
|
30
|
+
"""配置操作类型"""
|
31
|
+
SET = auto()
|
32
|
+
DELETE = auto()
|
33
|
+
RESET = auto()
|
34
|
+
|
35
|
+
@dataclass
|
36
|
+
class ConfigChangeEvent:
|
37
|
+
"""配置变更事件"""
|
38
|
+
key: str
|
39
|
+
old_value: Any
|
40
|
+
new_value: Any
|
41
|
+
operation: ConfigOperation
|
42
|
+
|
43
|
+
class ConfigValidator(Generic[T]):
|
44
|
+
"""配置验证器基类"""
|
45
|
+
def validate(self, value: Any) -> T:
|
46
|
+
"""验证并转换配置值"""
|
47
|
+
raise NotImplementedError
|
48
|
+
|
49
|
+
class IntValidator(ConfigValidator[int]):
|
50
|
+
"""整数验证器"""
|
51
|
+
def __init__(self, min_value: Optional[int] = None, max_value: Optional[int] = None):
|
52
|
+
self.min = min_value
|
53
|
+
self.max = max_value
|
54
|
+
|
55
|
+
def validate(self, value: Any) -> int:
|
56
|
+
try:
|
57
|
+
val = int(value)
|
58
|
+
if self.min is not None and val < self.min:
|
59
|
+
raise ConfigValidationError(f"值 {val} 小于最小值 {self.min}")
|
60
|
+
if self.max is not None and val > self.max:
|
61
|
+
raise ConfigValidationError(f"值 {val} 大于最大值 {self.max}")
|
62
|
+
return val
|
63
|
+
except (ValueError, TypeError) as e:
|
64
|
+
raise ConfigValidationError(f"无效的整数值: {value}") from e
|
65
|
+
|
66
|
+
class Config:
|
67
|
+
"""
|
68
|
+
增强版配置管理类,提供类型安全、线程安全的配置管理
|
69
|
+
|
70
|
+
主要特性:
|
71
|
+
- 类型安全的配置存取
|
72
|
+
- 配置变更监听和通知
|
73
|
+
- 配置验证和转换
|
74
|
+
- 多格式持久化支持
|
75
|
+
- 原子操作和事务支持
|
76
|
+
- 配置版本控制
|
77
|
+
- 环境变量集成
|
78
|
+
"""
|
79
|
+
|
80
|
+
def __init__(
|
81
|
+
self,
|
82
|
+
initial_config: Optional[Dict[str, Any]] = None,
|
83
|
+
validators: Optional[Dict[str, ConfigValidator]] = None,
|
84
|
+
config_dir: Optional[Union[str, Path]] = None,
|
85
|
+
env_prefix: Optional[str] = None
|
86
|
+
):
|
87
|
+
"""
|
88
|
+
初始化配置存储
|
89
|
+
|
90
|
+
参数:
|
91
|
+
initial_config: 初始配置字典
|
92
|
+
validators: 配置验证器字典 {key: validator}
|
93
|
+
config_dir: 配置文件存储目录
|
94
|
+
env_prefix: 环境变量前缀
|
95
|
+
"""
|
96
|
+
self._data = deepcopy(initial_config) if initial_config else {}
|
97
|
+
self._validators = validators or {}
|
98
|
+
self._lock = threading.Lock()
|
99
|
+
self._change_listeners = []
|
100
|
+
self._config_dir = Path(config_dir) if config_dir else None
|
101
|
+
self._env_prefix = f"{env_prefix}_" if env_prefix else ""
|
102
|
+
self._version = 1
|
103
|
+
self._transaction_stack = []
|
104
|
+
|
105
|
+
# 从环境变量加载配置
|
106
|
+
self._load_from_env()
|
107
|
+
|
108
|
+
def __str__(self) -> str:
|
109
|
+
"""返回配置的可读字符串表示"""
|
110
|
+
return json.dumps(self._data, indent=2, ensure_ascii=False)
|
111
|
+
|
112
|
+
def __repr__(self) -> str:
|
113
|
+
"""返回配置的正式表示"""
|
114
|
+
return f"Config({self._data})"
|
115
|
+
|
116
|
+
def __contains__(self, key: str) -> bool:
|
117
|
+
"""检查配置项是否存在"""
|
118
|
+
return key in self._data
|
119
|
+
|
120
|
+
def __len__(self) -> int:
|
121
|
+
"""返回配置项的数量"""
|
122
|
+
return len(self._data)
|
123
|
+
|
124
|
+
def __enter__(self):
|
125
|
+
"""进入事务上下文"""
|
126
|
+
self.begin_transaction()
|
127
|
+
return self
|
128
|
+
|
129
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
130
|
+
"""退出事务上下文"""
|
131
|
+
if exc_type is None:
|
132
|
+
self.commit_transaction()
|
133
|
+
else:
|
134
|
+
self.rollback_transaction()
|
135
|
+
|
136
|
+
@property
|
137
|
+
def version(self) -> int:
|
138
|
+
"""获取配置版本"""
|
139
|
+
return self._version
|
140
|
+
|
141
|
+
def begin_transaction(self):
|
142
|
+
"""开始一个配置事务"""
|
143
|
+
with self._lock:
|
144
|
+
self._transaction_stack.append(deepcopy(self._data))
|
145
|
+
|
146
|
+
def commit_transaction(self):
|
147
|
+
"""提交当前事务"""
|
148
|
+
with self._lock:
|
149
|
+
if not self._transaction_stack:
|
150
|
+
raise ConfigError("没有活跃的事务可提交")
|
151
|
+
self._transaction_stack.pop()
|
152
|
+
self._version += 1
|
153
|
+
|
154
|
+
def rollback_transaction(self):
|
155
|
+
"""回滚当前事务"""
|
156
|
+
with self._lock:
|
157
|
+
if not self._transaction_stack:
|
158
|
+
raise ConfigError("没有活跃的事务可回滚")
|
159
|
+
self._data = self._transaction_stack.pop()
|
160
|
+
|
161
|
+
def add_change_listener(self, listener: Callable[[ConfigChangeEvent], None]):
|
162
|
+
"""
|
163
|
+
添加配置变更监听器
|
164
|
+
|
165
|
+
参数:
|
166
|
+
listener: 回调函数,接收 ConfigChangeEvent 参数
|
167
|
+
"""
|
168
|
+
with self._lock:
|
169
|
+
if listener not in self._change_listeners:
|
170
|
+
self._change_listeners.append(listener)
|
171
|
+
|
172
|
+
def remove_change_listener(self, listener: Callable[[ConfigChangeEvent], None]):
|
173
|
+
"""移除配置变更监听器"""
|
174
|
+
with self._lock:
|
175
|
+
if listener in self._change_listeners:
|
176
|
+
self._change_listeners.remove(listener)
|
177
|
+
|
178
|
+
def _notify_change(self, event: ConfigChangeEvent):
|
179
|
+
"""通知所有监听器配置变更"""
|
180
|
+
with self._lock:
|
181
|
+
listeners = self._change_listeners.copy()
|
182
|
+
|
183
|
+
for listener in listeners:
|
184
|
+
try:
|
185
|
+
listener(event)
|
186
|
+
except Exception as e:
|
187
|
+
logger.error(f"配置变更通知错误: {e}", exc_info=True)
|
188
|
+
|
189
|
+
def get(self, key: str, default: Any = None, validate: bool = True) -> Any:
|
190
|
+
"""
|
191
|
+
获取配置项
|
192
|
+
|
193
|
+
参数:
|
194
|
+
key: 配置键名
|
195
|
+
default: 默认值
|
196
|
+
validate: 是否验证返回值
|
197
|
+
|
198
|
+
返回:
|
199
|
+
配置值或默认值
|
200
|
+
|
201
|
+
异常:
|
202
|
+
ConfigValidationError: 验证失败
|
203
|
+
"""
|
204
|
+
with self._lock:
|
205
|
+
value = self._data.get(key, default)
|
206
|
+
|
207
|
+
if validate and key in self._validators:
|
208
|
+
try:
|
209
|
+
return self._validators[key].validate(value)
|
210
|
+
except ConfigValidationError as e:
|
211
|
+
logger.warning(f"配置验证失败 [{key}]: {e}")
|
212
|
+
raise
|
213
|
+
|
214
|
+
return value
|
215
|
+
|
216
|
+
def set(self, key: str, value: Any, validate: bool = True) -> bool:
|
217
|
+
"""
|
218
|
+
设置配置项
|
219
|
+
|
220
|
+
参数:
|
221
|
+
key: 配置键名
|
222
|
+
value: 配置值
|
223
|
+
validate: 是否验证值
|
224
|
+
|
225
|
+
返回:
|
226
|
+
True 设置成功, False 设置失败
|
227
|
+
|
228
|
+
异常:
|
229
|
+
ConfigValidationError: 验证失败
|
230
|
+
ConfigLockedError: 配置被锁定
|
231
|
+
"""
|
232
|
+
if validate and key in self._validators:
|
233
|
+
value = self._validators[key].validate(value)
|
234
|
+
|
235
|
+
with self._lock:
|
236
|
+
if not self._transaction_stack:
|
237
|
+
raise ConfigError("必须在事务中修改配置")
|
238
|
+
|
239
|
+
old_value = self._data.get(key)
|
240
|
+
self._data[key] = value
|
241
|
+
|
242
|
+
# 通知变更
|
243
|
+
event = ConfigChangeEvent(
|
244
|
+
key=key,
|
245
|
+
old_value=old_value,
|
246
|
+
new_value=value,
|
247
|
+
operation=ConfigOperation.SET
|
248
|
+
)
|
249
|
+
self._notify_change(event)
|
250
|
+
|
251
|
+
return True
|
252
|
+
|
253
|
+
def delete(self, key: str) -> bool:
|
254
|
+
"""
|
255
|
+
删除配置项
|
256
|
+
|
257
|
+
参数:
|
258
|
+
key: 要删除的配置键名
|
259
|
+
|
260
|
+
返回:
|
261
|
+
True 删除成功, False 键不存在
|
262
|
+
|
263
|
+
异常:
|
264
|
+
ConfigLockedError: 配置被锁定
|
265
|
+
"""
|
266
|
+
with self._lock:
|
267
|
+
if not self._transaction_stack:
|
268
|
+
raise ConfigError("必须在事务中修改配置")
|
269
|
+
|
270
|
+
if key not in self._data:
|
271
|
+
return False
|
272
|
+
|
273
|
+
old_value = self._data[key]
|
274
|
+
del self._data[key]
|
275
|
+
|
276
|
+
# 通知变更
|
277
|
+
event = ConfigChangeEvent(
|
278
|
+
key=key,
|
279
|
+
old_value=old_value,
|
280
|
+
new_value=None,
|
281
|
+
operation=ConfigOperation.DELETE
|
282
|
+
)
|
283
|
+
self._notify_change(event)
|
284
|
+
|
285
|
+
return True
|
286
|
+
|
287
|
+
def has(self, key: str) -> bool:
|
288
|
+
"""检查配置项是否存在"""
|
289
|
+
with self._lock:
|
290
|
+
return key in self._data
|
291
|
+
|
292
|
+
def bulk_update(self, updates: Dict[str, Any], validate: bool = True) -> bool:
|
293
|
+
"""
|
294
|
+
批量更新配置
|
295
|
+
|
296
|
+
参数:
|
297
|
+
updates: 包含多个键值对的字典
|
298
|
+
validate: 是否验证值
|
299
|
+
|
300
|
+
返回:
|
301
|
+
True 更新成功
|
302
|
+
|
303
|
+
异常:
|
304
|
+
ConfigValidationError: 验证失败
|
305
|
+
ConfigLockedError: 配置被锁定
|
306
|
+
"""
|
307
|
+
if validate:
|
308
|
+
for key, value in updates.items():
|
309
|
+
if key in self._validators:
|
310
|
+
updates[key] = self._validators[key].validate(value)
|
311
|
+
|
312
|
+
with self._lock:
|
313
|
+
if not self._transaction_stack:
|
314
|
+
raise ConfigError("必须在事务中修改配置")
|
315
|
+
|
316
|
+
# 记录变更
|
317
|
+
changes = []
|
318
|
+
for key, value in updates.items():
|
319
|
+
old_value = self._data.get(key)
|
320
|
+
self._data[key] = value
|
321
|
+
changes.append(ConfigChangeEvent(
|
322
|
+
key=key,
|
323
|
+
old_value=old_value,
|
324
|
+
new_value=value,
|
325
|
+
operation=ConfigOperation.SET
|
326
|
+
))
|
327
|
+
|
328
|
+
# 批量通知变更
|
329
|
+
for event in changes:
|
330
|
+
self._notify_change(event)
|
331
|
+
|
332
|
+
return True
|
333
|
+
|
334
|
+
def reset(self, new_config: Optional[Dict[str, Any]] = None) -> None:
|
335
|
+
"""
|
336
|
+
重置所有配置
|
337
|
+
|
338
|
+
参数:
|
339
|
+
new_config: 新的配置字典 (可选,默认清空)
|
340
|
+
|
341
|
+
异常:
|
342
|
+
ConfigLockedError: 配置被锁定
|
343
|
+
"""
|
344
|
+
with self._lock:
|
345
|
+
if not self._transaction_stack:
|
346
|
+
raise ConfigError("必须在事务中修改配置")
|
347
|
+
|
348
|
+
# 记录所有变更(删除)
|
349
|
+
delete_events = []
|
350
|
+
for key in list(self._data.keys()):
|
351
|
+
delete_events.append(ConfigChangeEvent(
|
352
|
+
key=key,
|
353
|
+
old_value=self._data[key],
|
354
|
+
new_value=None,
|
355
|
+
operation=ConfigOperation.DELETE
|
356
|
+
))
|
357
|
+
|
358
|
+
# 重置配置
|
359
|
+
new_data = deepcopy(new_config) if new_config else {}
|
360
|
+
self._data = new_data
|
361
|
+
|
362
|
+
# 通知所有删除和新配置项
|
363
|
+
for event in delete_events:
|
364
|
+
self._notify_change(event)
|
365
|
+
|
366
|
+
for key, value in self._data.items():
|
367
|
+
self._notify_change(ConfigChangeEvent(
|
368
|
+
key=key,
|
369
|
+
old_value=None,
|
370
|
+
new_value=value,
|
371
|
+
operation=ConfigOperation.SET
|
372
|
+
))
|
373
|
+
|
374
|
+
def to_dict(self) -> Dict[str, Any]:
|
375
|
+
"""获取所有配置的深拷贝"""
|
376
|
+
with self._lock:
|
377
|
+
return deepcopy(self._data)
|
378
|
+
|
379
|
+
def save(self, filename: Optional[str] = None, format: str = 'json') -> bool:
|
380
|
+
"""
|
381
|
+
保存配置到文件
|
382
|
+
|
383
|
+
参数:
|
384
|
+
filename: 文件名 (可选,使用默认配置目录)
|
385
|
+
format: 文件格式 ('json', 'yaml')
|
386
|
+
|
387
|
+
返回:
|
388
|
+
True 保存成功, False 保存失败
|
389
|
+
"""
|
390
|
+
if filename is None and self._config_dir is None:
|
391
|
+
raise ConfigError("未指定文件名且未设置配置目录")
|
392
|
+
|
393
|
+
filepath = Path(filename) if filename else self._config_dir / f"config.{format}"
|
394
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
395
|
+
|
396
|
+
try:
|
397
|
+
with filepath.open('w', encoding='utf-8') as f:
|
398
|
+
if format == 'json':
|
399
|
+
json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
|
400
|
+
elif format == 'yaml':
|
401
|
+
import yaml
|
402
|
+
yaml.safe_dump(self.to_dict(), f, allow_unicode=True)
|
403
|
+
else:
|
404
|
+
raise ConfigError(f"不支持的格式: {format}")
|
405
|
+
return True
|
406
|
+
except Exception as e:
|
407
|
+
logger.error(f"保存配置失败: {e}", exc_info=True)
|
408
|
+
return False
|
409
|
+
|
410
|
+
@classmethod
|
411
|
+
def load(
|
412
|
+
cls,
|
413
|
+
filename: str,
|
414
|
+
validators: Optional[Dict[str, ConfigValidator]] = None,
|
415
|
+
config_dir: Optional[Union[str, Path]] = None,
|
416
|
+
env_prefix: Optional[str] = None
|
417
|
+
) -> 'Config':
|
418
|
+
"""
|
419
|
+
从文件加载配置
|
420
|
+
|
421
|
+
参数:
|
422
|
+
filename: 文件名
|
423
|
+
validators: 配置验证器
|
424
|
+
config_dir: 配置目录
|
425
|
+
env_prefix: 环境变量前缀
|
426
|
+
|
427
|
+
返回:
|
428
|
+
加载的 Config 实例
|
429
|
+
|
430
|
+
异常:
|
431
|
+
ConfigError: 加载失败
|
432
|
+
"""
|
433
|
+
filepath = Path(filename)
|
434
|
+
if not filepath.is_absolute() and config_dir is not None:
|
435
|
+
filepath = Path(config_dir) / filename
|
436
|
+
|
437
|
+
try:
|
438
|
+
with filepath.open('r', encoding='utf-8') as f:
|
439
|
+
if filepath.suffix.lower() == '.json':
|
440
|
+
data = json.load(f)
|
441
|
+
elif filepath.suffix.lower() in ('.yaml', '.yml'):
|
442
|
+
import yaml
|
443
|
+
data = yaml.safe_load(f)
|
444
|
+
else:
|
445
|
+
raise ConfigError(f"不支持的文件格式: {filepath.suffix}")
|
446
|
+
|
447
|
+
return cls(
|
448
|
+
initial_config=data,
|
449
|
+
validators=validators,
|
450
|
+
config_dir=config_dir,
|
451
|
+
env_prefix=env_prefix
|
452
|
+
)
|
453
|
+
except Exception as e:
|
454
|
+
raise ConfigError(f"加载配置失败: {e}") from e
|
455
|
+
|
456
|
+
def _load_from_env(self):
|
457
|
+
"""从环境变量加载配置"""
|
458
|
+
if not self._env_prefix:
|
459
|
+
return
|
460
|
+
|
461
|
+
for key, value in os.environ.items():
|
462
|
+
if key.startswith(self._env_prefix):
|
463
|
+
config_key = key[len(self._env_prefix):].lower()
|
464
|
+
try:
|
465
|
+
# 尝试解析JSON格式的环境变量
|
466
|
+
parsed_value = json.loads(value)
|
467
|
+
self._data[config_key] = parsed_value
|
468
|
+
except json.JSONDecodeError:
|
469
|
+
# 普通字符串值
|
470
|
+
self._data[config_key] = value
|
471
|
+
|
472
|
+
def register_validator(self, key: str, validator: ConfigValidator):
|
473
|
+
"""注册配置验证器"""
|
474
|
+
with self._lock:
|
475
|
+
self._validators[key] = validator
|
476
|
+
|
477
|
+
def unregister_validator(self, key: str):
|
478
|
+
"""移除配置验证器"""
|
479
|
+
with self._lock:
|
480
|
+
if key in self._validators:
|
481
|
+
del self._validators[key]
|
482
|
+
|
483
|
+
def get_validator(self, key: str) -> Optional[ConfigValidator]:
|
484
|
+
"""获取配置验证器"""
|
485
|
+
with self._lock:
|
486
|
+
return self._validators.get(key)
|
487
|
+
|
488
|
+
def validate_all(self) -> Dict[str, Union[Any, Exception]]:
|
489
|
+
"""验证所有配置项,返回验证结果字典"""
|
490
|
+
results = {}
|
491
|
+
with self._lock:
|
492
|
+
for key, validator in self._validators.items():
|
493
|
+
if key in self._data:
|
494
|
+
try:
|
495
|
+
results[key] = validator.validate(self._data[key])
|
496
|
+
except Exception as e:
|
497
|
+
results[key] = e
|
498
|
+
return results
|