mdbq 4.0.22__tar.gz → 4.0.24__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.
- {mdbq-4.0.22 → mdbq-4.0.24}/PKG-INFO +1 -1
- mdbq-4.0.24/mdbq/__version__.py +1 -0
- mdbq-4.0.22/mdbq/myconf/myconf2.py → mdbq-4.0.24/mdbq/myconf/myconf.py +259 -159
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq.egg-info/PKG-INFO +1 -1
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq.egg-info/SOURCES.txt +1 -1
- mdbq-4.0.22/mdbq/__version__.py +0 -1
- {mdbq-4.0.22 → mdbq-4.0.24}/README.txt +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/__init__.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/aggregation/__init__.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/aggregation/query_data.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/log/__init__.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/log/mylogger.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/myconf/__init__.py +0 -0
- /mdbq-4.0.22/mdbq/myconf/myconf.py → /mdbq-4.0.24/mdbq/myconf/myconf_bak.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/mysql/__init__.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/mysql/deduplicator.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/mysql/mysql.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/mysql/s_query.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/mysql/unique_.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/mysql/uploader.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/other/__init__.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/other/download_sku_picture.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/other/otk.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/other/pov_city.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/other/ua_sj.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/pbix/__init__.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/pbix/pbix_refresh.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/pbix/refresh_all.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/redis/__init__.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/redis/getredis.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/spider/__init__.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq/spider/aikucun.py +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq.egg-info/dependency_links.txt +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/mdbq.egg-info/top_level.txt +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/setup.cfg +0 -0
- {mdbq-4.0.22 → mdbq-4.0.24}/setup.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
VERSION = '4.0.24'
|
@@ -21,14 +21,16 @@ logger = mylogger.MyLogger(
|
|
21
21
|
T = TypeVar('T') # 类型变量
|
22
22
|
|
23
23
|
|
24
|
-
class
|
25
|
-
"""
|
24
|
+
class ConfigException(Exception):
|
25
|
+
"""配置异常基类"""
|
26
26
|
def __init__(self, message: str, file_path: Optional[Union[str, Path]] = None,
|
27
|
-
section: Optional[str] = None, key: Optional[str] = None
|
27
|
+
section: Optional[str] = None, key: Optional[str] = None,
|
28
|
+
original_error: Optional[Exception] = None):
|
28
29
|
self.message = message
|
29
30
|
self.file_path = str(file_path) if file_path else None
|
30
31
|
self.section = section
|
31
32
|
self.key = key
|
33
|
+
self.original_error = original_error
|
32
34
|
super().__init__(self._format_message())
|
33
35
|
|
34
36
|
def _format_message(self) -> str:
|
@@ -40,62 +42,69 @@ class ConfigError(Exception):
|
|
40
42
|
parts.append(f"节: [{self.section}]")
|
41
43
|
if self.key:
|
42
44
|
parts.append(f"键: {self.key}")
|
45
|
+
if self.original_error:
|
46
|
+
parts.append(f"原始错误: {str(self.original_error)}")
|
43
47
|
return " | ".join(parts)
|
44
48
|
|
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
|
-
class ConfigKeyNotFoundError(ConfigError):
|
90
|
-
"""配置键不存在异常"""
|
91
|
-
def __init__(self, file_path: Union[str, Path], section: str, key: str):
|
92
|
-
super().__init__(
|
93
|
-
f"配置键不存在",
|
49
|
+
@classmethod
|
50
|
+
def file_not_found(cls, file_path: Union[str, Path]) -> 'ConfigException':
|
51
|
+
"""配置文件不存在异常"""
|
52
|
+
return cls("配置文件不存在", file_path=file_path)
|
53
|
+
|
54
|
+
@classmethod
|
55
|
+
def read_error(cls, file_path: Union[str, Path], error: Exception) -> 'ConfigException':
|
56
|
+
"""读取配置文件失败异常"""
|
57
|
+
return cls("读取配置文件失败", file_path=file_path, original_error=error)
|
58
|
+
|
59
|
+
@classmethod
|
60
|
+
def write_error(cls, file_path: Union[str, Path], error: Exception) -> 'ConfigException':
|
61
|
+
"""写入配置文件失败异常"""
|
62
|
+
return cls("写入配置文件失败", file_path=file_path, original_error=error)
|
63
|
+
|
64
|
+
@classmethod
|
65
|
+
def value_error(cls, message: str, file_path: Union[str, Path],
|
66
|
+
section: Optional[str] = None, key: Optional[str] = None) -> 'ConfigException':
|
67
|
+
"""配置值无效异常"""
|
68
|
+
return cls(message, file_path=file_path, section=section, key=key)
|
69
|
+
|
70
|
+
@classmethod
|
71
|
+
def section_not_found(cls, file_path: Union[str, Path], section: str) -> 'ConfigException':
|
72
|
+
"""配置节不存在异常"""
|
73
|
+
return cls("配置节不存在", file_path=file_path, section=section)
|
74
|
+
|
75
|
+
@classmethod
|
76
|
+
def key_not_found(cls, file_path: Union[str, Path], section: str, key: str) -> 'ConfigException':
|
77
|
+
"""配置键不存在异常"""
|
78
|
+
return cls("配置键不存在", file_path=file_path, section=section, key=key)
|
79
|
+
|
80
|
+
@classmethod
|
81
|
+
def validation_error(cls, message: str, file_path: Union[str, Path],
|
82
|
+
section: Optional[str] = None, key: Optional[str] = None) -> 'ConfigException':
|
83
|
+
"""配置验证失败异常"""
|
84
|
+
return cls(f"配置验证失败: {message}", file_path=file_path, section=section, key=key)
|
85
|
+
|
86
|
+
@classmethod
|
87
|
+
def conversion_error(cls, value: Any, target_type: Type, file_path: Union[str, Path],
|
88
|
+
section: Optional[str] = None, key: Optional[str] = None) -> 'ConfigException':
|
89
|
+
"""类型转换失败异常"""
|
90
|
+
return cls(
|
91
|
+
f"无法将值 '{value}' 转换为类型 {target_type.__name__}",
|
94
92
|
file_path=file_path,
|
95
93
|
section=section,
|
96
94
|
key=key
|
97
95
|
)
|
98
96
|
|
97
|
+
@classmethod
|
98
|
+
def invalid_key_error(cls, key: str, file_path: Union[str, Path],
|
99
|
+
section: Optional[str] = None) -> 'ConfigException':
|
100
|
+
"""无效的键名异常"""
|
101
|
+
return cls(f"无效的键名: {key}", file_path=file_path, section=section, key=key)
|
102
|
+
|
103
|
+
@classmethod
|
104
|
+
def invalid_section_error(cls, section: str, file_path: Union[str, Path]) -> 'ConfigException':
|
105
|
+
"""无效的节名异常"""
|
106
|
+
return cls(f"无效的节名: {section}", file_path=file_path, section=section)
|
107
|
+
|
99
108
|
|
100
109
|
class CommentStyle(Enum):
|
101
110
|
"""配置文件支持的注释风格"""
|
@@ -143,14 +152,14 @@ class ConfigParser:
|
|
143
152
|
"""打开配置文件"""
|
144
153
|
file_path = Path(file_path)
|
145
154
|
if not file_path.exists() and not self.options.auto_create:
|
146
|
-
raise
|
155
|
+
raise ConfigException.file_not_found(file_path)
|
147
156
|
self._current_file = file_path
|
148
157
|
return self
|
149
158
|
|
150
159
|
def _ensure_file_open(self) -> None:
|
151
160
|
"""确保文件已打开"""
|
152
161
|
if self._current_file is None:
|
153
|
-
raise
|
162
|
+
raise ConfigException("未打开任何配置文件,请先调用 open() 方法")
|
154
163
|
|
155
164
|
def _is_comment_line(self, line: str) -> bool:
|
156
165
|
"""判断是否为注释行"""
|
@@ -204,7 +213,7 @@ class ConfigParser:
|
|
204
213
|
|
205
214
|
def _normalize_section(self, section: str) -> str:
|
206
215
|
"""标准化节名称"""
|
207
|
-
return section
|
216
|
+
return section.replace(' ', '').lower()
|
208
217
|
|
209
218
|
def _get_original_section(self, file_path: str, normalized_section: str) -> Optional[str]:
|
210
219
|
"""获取原始节名称"""
|
@@ -233,7 +242,7 @@ class ConfigParser:
|
|
233
242
|
self._comments_cache.clear()
|
234
243
|
self._section_map.clear()
|
235
244
|
|
236
|
-
def _convert_value(self, value: str, target_type: Type[T]) -> T:
|
245
|
+
def _convert_value(self, value: str, target_type: Type[T], file_path: Optional[Union[str, Path]] = None, key: Optional[str] = None) -> T:
|
237
246
|
"""转换配置值到指定类型"""
|
238
247
|
try:
|
239
248
|
if target_type == bool:
|
@@ -252,13 +261,14 @@ class ConfigParser:
|
|
252
261
|
if sep in value:
|
253
262
|
return tuple(item.strip() for item in value.split(sep) if item.strip())
|
254
263
|
return (value.strip(),)
|
255
|
-
elif target_type == set:
|
264
|
+
elif target_type == set or target_type == frozenset:
|
256
265
|
if not value.strip():
|
257
|
-
return set()
|
266
|
+
return set() if target_type == set else frozenset()
|
258
267
|
for sep in [',', ';', '|', ' ']:
|
259
268
|
if sep in value:
|
260
|
-
|
261
|
-
|
269
|
+
items = [item.strip() for item in value.split(sep) if item.strip()]
|
270
|
+
return set(items) if target_type == set else frozenset(items)
|
271
|
+
return set([value.strip()]) if target_type == set else frozenset([value.strip()])
|
262
272
|
elif target_type == dict:
|
263
273
|
if not value.strip():
|
264
274
|
return {}
|
@@ -268,12 +278,12 @@ class ConfigParser:
|
|
268
278
|
pairs = [pair.strip() for pair in value.split(sep) if pair.strip()]
|
269
279
|
for pair in pairs:
|
270
280
|
if '=' in pair:
|
271
|
-
|
272
|
-
result[
|
281
|
+
key_, val = pair.split('=', 1)
|
282
|
+
result[key_.strip()] = val.strip()
|
273
283
|
return result
|
274
284
|
if '=' in value:
|
275
|
-
|
276
|
-
return {
|
285
|
+
key_, val = value.split('=', 1)
|
286
|
+
return {key_.strip(): val.strip()}
|
277
287
|
return {}
|
278
288
|
elif target_type == int:
|
279
289
|
value = value.strip().lower()
|
@@ -292,10 +302,6 @@ class ConfigParser:
|
|
292
302
|
return value.encode('utf-8')
|
293
303
|
elif target_type == bytearray:
|
294
304
|
return bytearray(value.encode('utf-8'))
|
295
|
-
elif target_type == set:
|
296
|
-
return set(value.split(','))
|
297
|
-
elif target_type == frozenset:
|
298
|
-
return frozenset(value.split(','))
|
299
305
|
elif target_type == range:
|
300
306
|
parts = value.split(':')
|
301
307
|
if len(parts) == 2:
|
@@ -305,48 +311,38 @@ class ConfigParser:
|
|
305
311
|
raise ValueError("Invalid range format")
|
306
312
|
return target_type(value)
|
307
313
|
except (ValueError, TypeError) as e:
|
308
|
-
raise
|
309
|
-
f"无法将值 '{value}' 转换为类型 {target_type.__name__}",
|
310
|
-
file_path=None,
|
311
|
-
key=None
|
312
|
-
)
|
314
|
+
raise ConfigException.conversion_error(value, target_type, file_path, key=key)
|
313
315
|
|
314
|
-
def get_value(self,
|
315
|
-
|
316
|
-
|
316
|
+
def get_value(self, section: Optional[str] = None, key: str = None,
|
317
|
+
default: Any = None, value_type: Optional[Type[T]] = None,
|
318
|
+
file_path: Optional[Union[str, Path]] = None) -> T:
|
317
319
|
"""获取指定配置项的值"""
|
318
320
|
if file_path is None:
|
319
321
|
self._ensure_file_open()
|
320
322
|
file_path = self._current_file
|
321
323
|
if not self._validate_key(key):
|
322
|
-
raise
|
323
|
-
|
324
|
+
raise ConfigException.invalid_key_error(key, file_path, section)
|
324
325
|
config = self.read(file_path)
|
325
326
|
section = section or self.options.default_section
|
326
327
|
normalized_section = self._normalize_section(section)
|
327
|
-
|
328
328
|
original_section = self._get_original_section(str(file_path), normalized_section)
|
329
329
|
if original_section is None:
|
330
330
|
if default is not None:
|
331
331
|
return default
|
332
|
-
raise
|
333
|
-
|
332
|
+
raise ConfigException.section_not_found(file_path, section)
|
334
333
|
if key not in config[original_section]:
|
335
334
|
if default is not None:
|
336
335
|
return default
|
337
|
-
raise
|
338
|
-
|
336
|
+
raise ConfigException.key_not_found(file_path, original_section, key)
|
339
337
|
value = config[original_section][key]
|
340
|
-
|
341
338
|
if value_type is not None:
|
342
|
-
return self._convert_value(value, value_type)
|
343
|
-
|
339
|
+
return self._convert_value(value, value_type, file_path=file_path, key=key)
|
344
340
|
return value
|
345
341
|
|
346
|
-
def get_values(self, keys: List[Tuple[str, str]],
|
347
|
-
file_path: Optional[Union[str, Path]] = None,
|
342
|
+
def get_values(self, section: Optional[str] = None, keys: List[Tuple[str, str]] = None,
|
348
343
|
defaults: Optional[Dict[str, Any]] = None,
|
349
|
-
value_types: Optional[Dict[str, Type]] = None
|
344
|
+
value_types: Optional[Dict[str, Type]] = None,
|
345
|
+
file_path: Optional[Union[str, Path]] = None) -> Dict[str, Any]:
|
350
346
|
"""批量获取多个配置项的值"""
|
351
347
|
if file_path is None:
|
352
348
|
self._ensure_file_open()
|
@@ -358,14 +354,15 @@ class ConfigParser:
|
|
358
354
|
for section, key in keys:
|
359
355
|
try:
|
360
356
|
value = self.get_value(
|
361
|
-
file_path=file_path,
|
362
|
-
key=key,
|
363
357
|
section=section,
|
358
|
+
key=key,
|
364
359
|
default=defaults.get(key),
|
365
|
-
value_type=value_types.get(key)
|
360
|
+
value_type=value_types.get(key),
|
361
|
+
file_path=file_path
|
366
362
|
)
|
367
363
|
result[key] = value
|
368
|
-
except
|
364
|
+
except ConfigException as e:
|
365
|
+
logger.error(f"读取配置项失败: section={section}, key={key}, error={e}")
|
369
366
|
if key in defaults:
|
370
367
|
result[key] = defaults[key]
|
371
368
|
else:
|
@@ -373,11 +370,10 @@ class ConfigParser:
|
|
373
370
|
|
374
371
|
return result
|
375
372
|
|
376
|
-
def get_section_values(self, keys: List[str],
|
377
|
-
section: Optional[str] = None,
|
378
|
-
file_path: Optional[Union[str, Path]] = None,
|
373
|
+
def get_section_values(self, section: Optional[str] = None, keys: List[str] = None,
|
379
374
|
defaults: Optional[Dict[str, Any]] = None,
|
380
|
-
value_types: Optional[Dict[str, Type]] = None
|
375
|
+
value_types: Optional[Dict[str, Type]] = None,
|
376
|
+
file_path: Optional[Union[str, Path]] = None) -> Tuple[Any, ...]:
|
381
377
|
"""获取指定节点下多个键的值元组"""
|
382
378
|
if file_path is None:
|
383
379
|
self._ensure_file_open()
|
@@ -389,14 +385,15 @@ class ConfigParser:
|
|
389
385
|
for key in keys:
|
390
386
|
try:
|
391
387
|
value = self.get_value(
|
392
|
-
file_path=file_path,
|
393
|
-
key=key,
|
394
388
|
section=section,
|
389
|
+
key=key,
|
395
390
|
default=defaults.get(key),
|
396
|
-
value_type=value_types.get(key)
|
391
|
+
value_type=value_types.get(key),
|
392
|
+
file_path=file_path
|
397
393
|
)
|
398
394
|
result.append(value)
|
399
|
-
except
|
395
|
+
except ConfigException as e:
|
396
|
+
logger.error(f"读取配置项失败: section={section}, key={key}, error={e}")
|
400
397
|
if key in defaults:
|
401
398
|
result.append(defaults[key])
|
402
399
|
else:
|
@@ -404,27 +401,23 @@ class ConfigParser:
|
|
404
401
|
|
405
402
|
return tuple(result)
|
406
403
|
|
407
|
-
def set_value(self, key: str, value: Any,
|
408
|
-
|
409
|
-
file_path: Optional[Union[str, Path]] = None
|
410
|
-
value_type: Optional[Type] = None) -> None:
|
404
|
+
def set_value(self, section: Optional[str] = None, key: str = None, value: Any = None,
|
405
|
+
value_type: Optional[Type] = None,
|
406
|
+
file_path: Optional[Union[str, Path]] = None) -> None:
|
411
407
|
"""设置指定配置项的值"""
|
412
408
|
if file_path is None:
|
413
409
|
self._ensure_file_open()
|
414
410
|
file_path = self._current_file
|
415
411
|
if not self._validate_key(key):
|
416
|
-
raise
|
417
|
-
|
412
|
+
raise ConfigException.invalid_key_error(key, file_path, section)
|
413
|
+
section = section or self.options.default_section
|
418
414
|
original_lines = []
|
419
415
|
if file_path.exists():
|
420
416
|
with open(file_path, 'r', encoding=self.options.encoding) as file:
|
421
417
|
original_lines = file.readlines()
|
422
|
-
|
423
418
|
config = self.read(file_path)
|
424
|
-
|
425
419
|
if section not in config:
|
426
420
|
config[section] = {}
|
427
|
-
|
428
421
|
if value_type is not None:
|
429
422
|
try:
|
430
423
|
if value_type == bool:
|
@@ -435,43 +428,29 @@ class ConfigParser:
|
|
435
428
|
else:
|
436
429
|
value = value_type(value)
|
437
430
|
except (ValueError, TypeError) as e:
|
438
|
-
raise
|
439
|
-
f"无法将值 '{value}' 转换为类型 {value_type.__name__}",
|
440
|
-
file_path=file_path,
|
441
|
-
section=section,
|
442
|
-
key=key
|
443
|
-
)
|
444
|
-
|
431
|
+
raise ConfigException.conversion_error(value, value_type, file_path, section=section, key=key)
|
445
432
|
if isinstance(value, bool):
|
446
433
|
value = str(value).lower()
|
447
434
|
else:
|
448
435
|
value = str(value)
|
449
|
-
|
450
436
|
config[section][key] = value
|
451
|
-
|
452
437
|
try:
|
453
438
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
454
|
-
|
455
439
|
with open(file_path, 'w', encoding=self.options.encoding) as file:
|
456
440
|
current_section = self.options.default_section
|
457
441
|
section_separators = {}
|
458
|
-
|
459
442
|
for line in original_lines:
|
460
443
|
stripped_line = line.strip()
|
461
|
-
|
462
444
|
if not stripped_line:
|
463
445
|
file.write(line)
|
464
446
|
continue
|
465
|
-
|
466
447
|
if stripped_line.startswith('[') and stripped_line.endswith(']'):
|
467
448
|
current_section = stripped_line[1:-1]
|
468
449
|
file.write(line)
|
469
450
|
continue
|
470
|
-
|
471
451
|
if self._is_comment_line(stripped_line):
|
472
452
|
file.write(line)
|
473
453
|
continue
|
474
|
-
|
475
454
|
key_value = self._split_key_value(stripped_line)
|
476
455
|
if key_value:
|
477
456
|
orig_key, orig_value = key_value
|
@@ -479,7 +458,6 @@ class ConfigParser:
|
|
479
458
|
if sep in line:
|
480
459
|
section_separators.setdefault(current_section, {})[orig_key] = sep
|
481
460
|
break
|
482
|
-
|
483
461
|
if current_section == section and orig_key == key:
|
484
462
|
separator = section_separators.get(current_section, {}).get(orig_key, self.options.separators[0])
|
485
463
|
comment = ''
|
@@ -493,15 +471,47 @@ class ConfigParser:
|
|
493
471
|
file.write(line)
|
494
472
|
else:
|
495
473
|
file.write(line)
|
496
|
-
|
497
474
|
if section not in [line.strip()[1:-1] for line in original_lines if line.strip().startswith('[') and line.strip().endswith(']')]:
|
498
475
|
file.write(f'\n[{section}]\n')
|
499
476
|
file.write(f'{key}={value}\n')
|
500
|
-
|
501
477
|
self._clear_cache(str(file_path))
|
502
|
-
|
503
478
|
except Exception as e:
|
504
|
-
raise
|
479
|
+
raise ConfigException.write_error(file_path, e)
|
480
|
+
|
481
|
+
def set_values(self, section: Optional[str] = None, values: Dict[str, Any] = None,
|
482
|
+
value_types: Optional[Dict[str, Type]] = None,
|
483
|
+
file_path: Optional[Union[str, Path]] = None) -> None:
|
484
|
+
"""批量设置多个配置项的值"""
|
485
|
+
for key, value in values.items():
|
486
|
+
value_type = value_types.get(key) if value_types else None
|
487
|
+
self.set_value(section, key, value, value_type, file_path)
|
488
|
+
|
489
|
+
def validate_config(self, section: Optional[str] = None, schema: Dict[str, Type] = None,
|
490
|
+
file_path: Optional[Union[str, Path]] = None) -> bool:
|
491
|
+
"""验证配置是否符合指定的模式"""
|
492
|
+
config = self.read(file_path)
|
493
|
+
if section:
|
494
|
+
if section not in config:
|
495
|
+
return False
|
496
|
+
for key, expected_type in schema.items():
|
497
|
+
if key not in config[section]:
|
498
|
+
return False
|
499
|
+
try:
|
500
|
+
self._convert_value(config[section][key], expected_type)
|
501
|
+
except ConfigException:
|
502
|
+
return False
|
503
|
+
else:
|
504
|
+
for section, keys in schema.items():
|
505
|
+
if section not in config:
|
506
|
+
return False
|
507
|
+
for key, expected_type in keys.items():
|
508
|
+
if key not in config[section]:
|
509
|
+
return False
|
510
|
+
try:
|
511
|
+
self._convert_value(config[section][key], expected_type)
|
512
|
+
except ConfigException:
|
513
|
+
return False
|
514
|
+
return True
|
505
515
|
|
506
516
|
def read(self, file_path: Optional[Union[str, Path]] = None) -> Dict[str, Any]:
|
507
517
|
"""读取配置文件内容"""
|
@@ -517,7 +527,7 @@ class ConfigParser:
|
|
517
527
|
|
518
528
|
if not file_path.exists():
|
519
529
|
if not self.options.auto_create:
|
520
|
-
raise
|
530
|
+
raise ConfigException.file_not_found(file_path)
|
521
531
|
logger.info(f'配置文件不存在,将创建: {file_path}')
|
522
532
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
523
533
|
file_path.touch()
|
@@ -540,11 +550,7 @@ class ConfigParser:
|
|
540
550
|
if stripped_line.startswith('[') and stripped_line.endswith(']'):
|
541
551
|
current_section = stripped_line[1:-1]
|
542
552
|
if not self._validate_key(current_section):
|
543
|
-
raise
|
544
|
-
f"无效的节名: {current_section}",
|
545
|
-
file_path=file_path,
|
546
|
-
section=current_section
|
547
|
-
)
|
553
|
+
raise ConfigException.invalid_section_error(current_section, file_path)
|
548
554
|
self._update_section_map(str(file_path), current_section)
|
549
555
|
if current_section not in config:
|
550
556
|
config[current_section] = {}
|
@@ -557,12 +563,7 @@ class ConfigParser:
|
|
557
563
|
if key_value:
|
558
564
|
key, value = key_value
|
559
565
|
if not self._validate_key(key):
|
560
|
-
raise
|
561
|
-
f"无效的键名: {key}",
|
562
|
-
file_path=file_path,
|
563
|
-
section=current_section,
|
564
|
-
key=key
|
565
|
-
)
|
566
|
+
raise ConfigException.invalid_key_error(key, file_path, current_section)
|
566
567
|
value, comment = self._extract_comment(value)
|
567
568
|
|
568
569
|
if self.options.strip_values:
|
@@ -579,42 +580,141 @@ class ConfigParser:
|
|
579
580
|
return config
|
580
581
|
|
581
582
|
except Exception as e:
|
582
|
-
raise
|
583
|
+
raise ConfigException.read_error(file_path, e)
|
583
584
|
|
584
585
|
|
585
586
|
def main() -> None:
|
586
587
|
"""示例用法"""
|
587
|
-
config_file = Path('/Users/xigua/
|
588
|
-
|
588
|
+
config_file = Path('/Users/xigua/spd_副本.txt')
|
589
|
+
|
590
|
+
# 创建配置解析器实例
|
591
|
+
parser = ConfigParser()
|
592
|
+
# 设置配置选项
|
593
|
+
options = ConfigOptions(
|
594
|
+
comment_styles=[CommentStyle.HASH, CommentStyle.DOUBLE_SLASH],
|
595
|
+
encoding='utf-8',
|
596
|
+
auto_create=True,
|
597
|
+
strip_values=True,
|
598
|
+
preserve_comments=True,
|
599
|
+
default_section='DEFAULT',
|
600
|
+
separators=['=', ':', ':'],
|
601
|
+
cache_ttl=300,
|
602
|
+
validate_keys=True,
|
603
|
+
key_pattern=r'^[a-zA-Z0-9_\-\.]+$',
|
604
|
+
case_sensitive=False
|
605
|
+
)
|
606
|
+
parser = ConfigParser(options)
|
607
|
+
|
589
608
|
# 方式1:使用上下文管理器
|
590
609
|
with ConfigParser() as parser:
|
591
610
|
parser.open(config_file)
|
611
|
+
# 读取配置 - 最常用的参数放在前面
|
592
612
|
host, port, username, password = parser.get_section_values(
|
593
|
-
|
594
|
-
|
613
|
+
section='mysql', # 最常用的section参数放在前面
|
614
|
+
keys=['host', 'port', 'username', 'password']
|
595
615
|
)
|
596
|
-
print("
|
597
|
-
|
598
|
-
parser.set_value(
|
599
|
-
|
600
|
-
|
616
|
+
print("2.1 使用上下文管理器 读取结果:", host, port, username, password)
|
617
|
+
# 修改配置
|
618
|
+
parser.set_value(
|
619
|
+
section='mysql',
|
620
|
+
key='username',
|
621
|
+
value='root'
|
622
|
+
)
|
623
|
+
parser.set_value(
|
624
|
+
section='mysql',
|
625
|
+
key='port',
|
626
|
+
value=3306,
|
627
|
+
value_type=int
|
628
|
+
)
|
629
|
+
|
601
630
|
# 方式2:链式调用
|
602
631
|
parser = ConfigParser()
|
603
632
|
host, port, username, password = parser.open(config_file).get_section_values(
|
604
|
-
|
605
|
-
|
633
|
+
section='mysql',
|
634
|
+
keys=['host', 'port', 'username', 'password']
|
606
635
|
)
|
607
|
-
print("
|
608
|
-
|
636
|
+
print("2.2 链式调用 读取结果:", host, port, username, password)
|
637
|
+
|
609
638
|
# 方式3:传统方式
|
610
639
|
parser = ConfigParser()
|
611
640
|
host, port, username, password = parser.get_section_values(
|
612
|
-
file_path=config_file,
|
613
641
|
section='mysql',
|
614
|
-
keys=['host', 'port', 'username', 'password']
|
642
|
+
keys=['host', 'port', 'username', 'password'],
|
643
|
+
file_path=config_file # 文件路径参数放在最后
|
644
|
+
)
|
645
|
+
print("2.3 传统方式 读取结果:", host, port, username, password)
|
646
|
+
|
647
|
+
# 3.1 读取单个值 - 最常用的参数放在前面
|
648
|
+
value = parser.get_value(
|
649
|
+
section='mysql',
|
650
|
+
key='host',
|
651
|
+
default='localhost', # 默认值参数放在中间
|
652
|
+
value_type=str,
|
653
|
+
file_path=config_file
|
654
|
+
)
|
655
|
+
print("3.1 单个值读取结果:", value)
|
656
|
+
|
657
|
+
# 3.2 批量读取多个值 - 最常用的参数放在前面
|
658
|
+
values = parser.get_values(
|
659
|
+
section='mysql',
|
660
|
+
keys=[('host', 'host'), ('host', 'port')],
|
661
|
+
defaults={'host': 'localhost', 'port': 3306},
|
662
|
+
value_types={'port': int},
|
663
|
+
file_path=config_file
|
664
|
+
)
|
665
|
+
print("3.2 批量读取多个值结果:", values)
|
666
|
+
|
667
|
+
# 3.3 读取整个配置
|
668
|
+
config = parser.read(config_file)
|
669
|
+
print("3.3 读取完整配置:", config)
|
670
|
+
|
671
|
+
# 4.1 写入单个值 - 最常用的参数放在前面
|
672
|
+
parser.set_value(
|
673
|
+
section='mysql',
|
674
|
+
key='host',
|
675
|
+
value='127.0.0.1',
|
676
|
+
value_type=str,
|
677
|
+
file_path=config_file
|
678
|
+
)
|
679
|
+
print("4.1 写入单个值结果:", config_file)
|
680
|
+
|
681
|
+
# 4.2 写入不同类型的数据 - 最常用的参数放在前面
|
682
|
+
parser.open(config_file)
|
683
|
+
parser.set_value(
|
684
|
+
section='mysql',
|
685
|
+
key='port',
|
686
|
+
value=3306,
|
687
|
+
value_type=int
|
688
|
+
)
|
689
|
+
parser.set_value(
|
690
|
+
section='mysql',
|
691
|
+
key='debug',
|
692
|
+
value=True,
|
693
|
+
value_type=bool
|
694
|
+
)
|
695
|
+
parser.set_value(
|
696
|
+
section='mysql',
|
697
|
+
key='servers',
|
698
|
+
value=['server1', 'server2'],
|
699
|
+
value_type=list
|
615
700
|
)
|
616
|
-
print("
|
701
|
+
print("4.2 写入不同类型的数据")
|
702
|
+
|
703
|
+
try:
|
704
|
+
# 尝试读取不存在的配置项 - 最常用的参数放在前面
|
705
|
+
parser.get_value(
|
706
|
+
section='mysql',
|
707
|
+
key='nonexistent'
|
708
|
+
)
|
709
|
+
except ConfigException as e:
|
710
|
+
if "配置键不存在" in str(e):
|
711
|
+
print("5.1 错误处理 配置键不存在错误:", e)
|
712
|
+
elif "配置节不存在" in str(e):
|
713
|
+
print("5.2 错误处理 配置节不存在错误:", e)
|
714
|
+
else:
|
715
|
+
print("5.3 错误处理 配置错误:", e)
|
617
716
|
|
618
717
|
|
619
718
|
if __name__ == '__main__':
|
620
719
|
main()
|
720
|
+
|
mdbq-4.0.22/mdbq/__version__.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
VERSION = '4.0.22'
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|