确定把# E:\AI_System\core\config.py
import os
import json
import logging
import configparser
from pathlib import Path
from typing import Dict, Any, Optional, Union, List, Tuple
from dotenv import load_dotenv
import re
class CoreConfig:
"""
重构后的核心配置管理类
优化点:
1. 强类型验证和转换
2. 自动创建缺失目录
3. 配置变更追踪
4. 更安全的敏感数据处理
5. 更健壮的错误处理
"""
def __init__(self,
config_path: Optional[Union[str, Path]] = None,
env_prefix: str = "APP_",
default_config: Optional[Dict[str, Any]] = None):
"""
初始化配置管理器
:param config_path: 配置文件路径(支持.json, .ini, .env)
:param env_prefix: 环境变量前缀
:param default_config: 默认配置字典
"""
# 初始化日志
self.logger = logging.getLogger('CoreConfig')
self._setup_logger()
# 设置实例变量
self.env_prefix = env_prefix
self.config_path = self._normalize_path(config_path)
self.config_data = default_config or {}
self.config_modified = False
self.original_config = {}
# 自动设置基础目录
self.BASE_DIR = self._determine_base_dir()
self._set_default_paths()
self.log(f"📋 初始化配置管理器 | 环境前缀: {env_prefix} | 基础目录: {self.BASE_DIR}")
# 加载配置
self.load_configuration()
# 确保所有配置目录存在
self._ensure_directories()
def _setup_logger(self):
"""配置日志记录器"""
if not self.logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)
self.logger.propagate = False
def _normalize_path(self, path: Optional[Union[str, Path]]) -> Optional[Path]:
"""将路径转换为Path对象并解析为绝对路径"""
if path is None:
return None
if isinstance(path, str):
path = Path(path)
if not path.is_absolute():
# 尝试相对于基础目录解析
base_dir = self._determine_base_dir()
return (base_dir / path).resolve()
return path.resolve()
def _determine_base_dir(self) -> Path:
"""确定项目基础目录"""
# 1. 尝试从环境变量获取
base_dir_env = os.getenv(f"{self.env_prefix}BASE_DIR")
if base_dir_env:
return Path(base_dir_env).resolve()
# 2. 尝试基于当前文件位置计算
try:
return Path(__file__).parent.parent.resolve()
except NameError:
# 在交互式环境中运行时
return Path.cwd().resolve()
# 3. 默认使用当前工作目录
return Path.cwd().resolve()
def _set_default_paths(self):
"""设置默认路径配置"""
# 基本路径
self.config_data.setdefault("BASE_DIR", str(self.BASE_DIR))
# 常用目录
self.config_data.setdefault("LOG_DIR", str(self.BASE_DIR / "logs"))
self.config_data.setdefault("CONFIG_DIR", str(self.BASE_DIR / "config"))
self.config_data.setdefault("CACHE_DIR", str(self.BASE_DIR / "cache"))
self.config_data.setdefault("DATA_DIR", str(self.BASE_DIR / "data"))
self.config_data.setdefault("MODEL_DIR", str(self.BASE_DIR / "models"))
def log(self, message: str, level: str = "info"):
"""记录日志"""
log_levels = {
"debug": self.logger.debug,
"info": self.logger.info,
"warning": self.logger.warning,
"error": self.logger.error
}
log_func = log_levels.get(level.lower(), self.logger.info)
log_func(message)
def load_configuration(self):
"""加载所有配置源"""
# 记录原始配置用于变更检测
self.original_config = self.config_data.copy()
# 1. 加载环境变量
self._load_environment_vars()
# 2. 加载配置文件(如果存在)
if self.config_path and self.config_path.exists():
self._load_config_file()
elif self.config_path:
self.log(f"⚠️ 配置文件不存在: {self.config_path}", "warning")
# 3. 应用路径配置
self._apply_path_configurations()
self.log(f"✅ 配置加载完成 | 条目数: {len(self.config_data)}")
# 检测配置变更
self._detect_config_changes()
def _apply_path_configurations(self):
"""应用路径配置并转换为Path对象"""
# 更新基础目录(如果配置中有)
if "BASE_DIR" in self.config_data:
self.BASE_DIR = Path(self.config_data["BASE_DIR"]).resolve()
# 确保所有路径配置都是绝对路径
path_keys = ["LOG_DIR", "CONFIG_DIR", "CACHE_DIR", "DATA_DIR", "MODEL_DIR"]
for key in path_keys:
if key in self.config_data:
path_str = self.config_data[key]
if not Path(path_str).is_absolute():
# 转换为相对于BASE_DIR的路径
self.config_data[key] = str(self.BASE_DIR / path_str)
def _load_environment_vars(self):
"""加载环境变量"""
self.log("🔍 加载环境变量...")
# 从CONFIG_DIR加载.env文件(如果存在)
env_file = Path(self.config_data.get("CONFIG_DIR", self.BASE_DIR / "config")) / ".env"
if env_file.exists():
load_dotenv(env_file)
self.log(f" - 加载环境变量文件: {env_file}")
# 获取所有以指定前缀开头的环境变量
for key, value in os.environ.items():
if key.startswith(self.env_prefix):
config_key = key[len(self.env_prefix):].lower()
parsed_value = self._parse_value(value)
self.config_data[config_key] = parsed_value
self.log(f" - 加载环境变量: {config_key} = {self._mask_secret(config_key, parsed_value)}")
def _load_config_file(self):
"""根据文件扩展名加载配置文件"""
suffix = self.config_path.suffix.lower()
self.log(f"📄 加载配置文件: {self.config_path} (类型: {suffix[1:]})")
try:
if suffix == '.json':
self._load_json_config()
elif suffix in ('.ini', '.cfg'):
self._load_ini_config()
elif suffix == '.env':
# .env文件已由_load_environment_vars处理
self.log(" - .env文件已由环境变量加载器处理")
else:
self.log(f"❌ 不支持的配置文件类型: {suffix}", "error")
except Exception as e:
self.log(f"❌ 配置文件加载失败: {str(e)}", "error")
self.log(f"⚠️ 使用默认配置继续运行", "warning")
def _load_json_config(self):
"""加载JSON配置文件"""
with open(self.config_path, 'r', encoding='utf-8') as f:
json_data = json.load(f)
self._merge_config(json_data)
def _load_ini_config(self):
"""加载INI配置文件"""
parser = configparser.ConfigParser()
parser.read(self.config_path)
config_dict = {}
for section in parser.sections():
section_dict = {}
for key, value in parser.items(section):
section_dict[key] = self._parse_value(value)
config_dict[section] = section_dict
self._merge_config(config_dict)
def _merge_config(self, new_config: Dict[str, Any]):
"""合并新配置到现有配置"""
for key, value in new_config.items():
# 处理嵌套配置
if isinstance(value, dict) and key in self.config_data and isinstance(self.config_data[key], dict):
# 深度合并字典
self._deep_merge_dict(self.config_data[key], value)
else:
self.config_data[key] = value
self.log(f" - 加载配置项: {key} = {self._mask_secret(key, value)}")
def _deep_merge_dict(self, base: Dict[str, Any], new: Dict[str, Any]):
"""深度合并两个字典"""
for key, value in new.items():
if key in base and isinstance(base[key], dict) and isinstance(value, dict):
self._deep_merge_dict(base[key], value)
else:
base[key] = value
def _parse_value(self, value: str) -> Any:
"""智能解析字符串值为合适的Python类型"""
# 处理空值
if value is None or value.strip() == "":
return None
# 尝试解析为布尔值
lower_val = value.lower()
if lower_val in ('true', 'yes', 'on', '1'):
return True
if lower_val in ('false', 'no', 'off', '0'):
return False
# 尝试解析为整数
try:
return int(value)
except ValueError:
pass
# 尝试解析为浮点数
try:
return float(value)
except ValueError:
pass
# 尝试解析为列表(支持逗号分隔和JSON数组格式)
if ',' in value:
# 简单逗号分隔的列表
return [self._parse_value(item.strip()) for item in value.split(',')]
if value.startswith('[') and value.endswith(']'):
# JSON格式的数组
try:
return json.loads(value)
except json.JSONDecodeError:
pass
# 尝试解析为字典(JSON对象)
if value.startswith('{') and value.endswith('}'):
try:
return json.loads(value)
except json.JSONDecodeError:
pass
# 返回原始字符串
return value
def _mask_secret(self, key: str, value: Any) -> str:
"""对敏感信息进行掩码处理"""
# 检测敏感键名
sensitive_keys = ['secret', 'password', 'key', 'token', 'credential', 'auth']
if any(s in key.lower() for s in sensitive_keys):
if isinstance(value, str) and value:
# 保留部分可见字符用于调试
if len(value) <= 3:
return '***'
return value[:1] + '***' + value[-1:]
return '******' if value else '空'
if isinstance(value, list):
return f"[列表, 长度: {len(value)}]"
if isinstance(value, dict):
return f"{{字典, 键数: {len(value)}}}"
if isinstance(value, str) and len(value) > 50:
return f"字符串[{len(value)}字符]"
return str(value)
def get(self, key: str, default: Any = None, required: bool = False) -> Any:
"""
获取配置值,支持点分路径 (如: 'database.host')
:param key: 配置键名
:param default: 默认值(如果键不存在)
:param required: 是否为必需项,如果必需且不存在则抛出异常
:return: 配置值
"""
keys = key.split('.')
current = self.config_data
for k in keys:
if isinstance(current, dict) and k in current:
current = current[k]
else:
if required:
raise KeyError(f"必需的配置项缺失: {key}")
return default
return current
def get_path(self, key: str, default: Optional[Union[str, Path]] = None) -> Path:
"""
获取路径配置项,确保返回Path对象
:param key: 配置键名
:param default: 默认路径
:return: Path对象
"""
path_str = self.get(key, default)
if path_str is None:
raise ValueError(f"路径配置项 {key} 未设置")
path = Path(path_str)
if not path.is_absolute():
# 转换为相对于BASE_DIR的路径
return self.BASE_DIR / path
return path
def set(self, key: str, value: Any, persist: bool = False):
"""
设置配置值
:param key: 配置键名
:param value: 配置值
:param persist: 是否持久化到配置文件
"""
keys = key.split('.')
current = self.config_data
# 遍历创建嵌套字典
for i, k in enumerate(keys[:-1]):
if k not in current:
current[k] = {}
elif not isinstance(current[k], dict):
# 如果当前值不是字典,则覆盖为字典
current[k] = {}
current = current[k]
# 设置最终值
last_key = keys[-1]
current[last_key] = value
self.config_modified = True
self.log(f"⚙️ 设置配置: {key} = {self._mask_secret(last_key, value)}")
# 持久化到文件
if persist and self.config_path:
self.save_config()
def save_config(self, path: Optional[Union[str, Path]] = None):
"""
保存配置到文件
:param path: 可选的自定义保存路径
:return: 是否保存成功
"""
save_path = self._normalize_path(path) if path else self.config_path
if not save_path:
self.log("❌ 保存失败: 未指定配置文件路径", "error")
return False
# 确保目录存在
save_path.parent.mkdir(parents=True, exist_ok=True)
suffix = save_path.suffix.lower()
try:
if suffix == '.json':
with open(save_path, 'w', encoding='utf-8') as f:
json.dump(self.config_data, f, indent=2, ensure_ascii=False)
elif suffix in ('.ini', '.cfg'):
self._save_ini_config(save_path)
else:
self.log(f"❌ 不支持的文件格式: {suffix}", "error")
return False
self.log(f"💾 配置已保存到: {save_path}")
self.config_modified = False
# 更新原始配置
self.original_config = self.config_data.copy()
return True
except Exception as e:
self.log(f"❌ 配置保存失败: {str(e)}", "error")
return False
def _save_ini_config(self, path: Path):
"""保存为INI格式配置文件"""
parser = configparser.ConfigParser()
# 添加DEFAULT节
parser.add_section('DEFAULT')
# 递归处理嵌套字典
def add_section(data, section_name=None):
if section_name:
if not parser.has_section(section_name):
parser.add_section(section_name)
for key, value in data.items():
if isinstance(value, dict):
# 递归处理嵌套字典
new_section = f"{section_name}.{key}" if section_name else key
add_section(value, new_section)
else:
# 处理非字典值
if section_name:
parser.set(section_name, key, str(value))
else:
parser.set('DEFAULT', key, str(value))
add_section(self.config_data)
with open(path, 'w', encoding='utf-8') as f:
parser.write(f)
def reload(self):
"""重新加载所有配置源"""
self.log("🔄 重新加载配置...")
# 保留当前配置作为基础
current_config = self.config_data.copy()
self.config_data = {}
self.load_configuration()
# 合并当前配置(优先保留当前设置)
self._deep_merge_dict(self.config_data, current_config)
self.log("✅ 配置重新加载完成")
def to_dict(self) -> Dict[str, Any]:
"""返回当前配置的完整字典(深度拷贝)"""
return json.loads(json.dumps(self.config_data))
def _ensure_directories(self):
"""确保所有配置目录存在"""
dir_keys = ["LOG_DIR", "CONFIG_DIR", "CACHE_DIR", "DATA_DIR", "MODEL_DIR"]
for key in dir_keys:
path_str = self.get(key)
if path_str:
path = Path(path_str)
if not path.exists():
path.mkdir(parents=True, exist_ok=True)
self.log(f"📁 创建目录: {path}")
def _detect_config_changes(self):
"""检测配置变更并记录"""
changed = []
added = []
removed = []
# 检测新增配置项
for key in self.config_data:
if key not in self.original_config:
added.append(key)
# 检测删除的配置项
for key in self.original_config:
if key not in self.config_data:
removed.append(key)
# 检测值变更
for key in self.config_data:
if key in self.original_config and self.config_data[key] != self.original_config[key]:
changed.append(key)
# 记录变更
if added or removed or changed:
self.log(f"🔄 配置变更检测: 新增 {len(added)} 项, 删除 {len(removed)} 项, 修改 {len(changed)} 项")
if added:
self.log(f" ➕ 新增配置: {', '.join(added)}")
if removed:
self.log(f" ➖ 删除配置: {', '.join(removed)}")
if changed:
self.log(f" ✏️ 修改配置: {', '.join(changed)}")
def __getitem__(self, key: str) -> Any:
"""支持字典式访问"""
return self.get(key)
def __setitem__(self, key: str, value: Any):
"""支持字典式设置"""
self.set(key, value)
def __contains__(self, key: str) -> bool:
"""检查配置项是否存在"""
return self.get(key) is not None
def __str__(self) -> str:
"""返回配置的字符串表示(掩码敏感信息)"""
masked_data = {}
for key, value in self.config_data.items():
masked_data[key] = self._mask_secret(key, value)
return json.dumps(masked_data, indent=2, ensure_ascii=False)
# 创建全局系统配置实例
system_config = CoreConfig(
config_path=Path(__file__).parent.parent / "config" / "system_config.json",
env_prefix="AI_SYSTEM_"
)
# 测试函数
def run_config_tests():
"""运行配置管理器测试"""
# 设置测试日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
print("=" * 50)
print("核心配置管理器测试")
print("=" * 50)
# 创建临时测试目录
import tempfile
with tempfile.TemporaryDirectory() as temp_dir:
test_dir = Path(temp_dir)
# 创建测试配置文件
config_json = {
"database": {
"host": "localhost",
"port": 5432,
"username": "admin",
"password": "db_secret"
},
"logging": {
"level": "INFO",
"path": "app_logs" # 相对路径
},
"features": ["auth", "caching", "analytics"]
}
test_config_path = test_dir / "test_config.json"
with open(test_config_path, "w") as f:
json.dump(config_json, f)
# 设置环境变量
os.environ["APP_API_KEY"] = "env_secret_key"
os.environ["APP_DATABASE_PORT"] = "6432" # 覆盖配置文件中的端口
try:
# 创建配置管理器实例
config = CoreConfig(
config_path=test_config_path,
env_prefix="APP_",
default_config={
"app_name": "MyApp",
"version": "1.0.0"
}
)
# 测试基础功能
print("\n=== 基础功能测试 ===")
print(f"基础目录: {config.BASE_DIR}")
print(f"日志目录: {config.get_path('LOG_DIR')}")
print(f"数据库主机: {config.get('database.host')}")
print(f"数据库端口: {config.get('database.port')} (应被环境变量覆盖)")
print(f"API密钥: {config.get('api_key')} (来自环境变量)")
print(f"应用名称: {config.get('app_name')} (来自默认配置)")
print(f"日志级别: {config.get('logging.level')}")
print(f"功能列表: {config.get('features')}")
# 测试路径解析
print("\n=== 路径解析测试 ===")
log_path = config.get_path("logging.path")
print(f"日志路径 (绝对路径): {log_path}")
print(f"日志路径是否存在: {log_path.exists()}")
# 测试配置设置
print("\n=== 配置设置测试 ===")
config.set("new_feature.enabled", True)
config.set("database.password", "new_secret", persist=True)
print(f"新功能状态: {config.get('new_feature.enabled')}")
# 测试保存和重新加载
print("\n=== 保存与重载测试 ===")
saved_path = test_dir / "saved_config.json"
config.save_config(saved_path)
print(f"配置已保存到: {saved_path}")
# 修改配置并重新加载
config.set("app_name", "UpdatedApp")
print(f"修改后应用名称: {config.get('app_name')}")
config.reload()
print(f"重新加载后应用名称: {config.get('app_name')} (应恢复原值)")
# 测试敏感信息掩码
print("\n=== 敏感信息掩码测试 ===")
print(f"数据库密码: {config.get('database.password')} (实际值)")
print(f"掩码后表示: {config._mask_secret('database.password', config.get('database.password'))}")
# 测试配置变更检测
print("\n=== 配置变更检测测试 ===")
config.set("test_change", "value")
config.set("app_name", "ChangedApp")
config.reload() # 会检测变更
except Exception as e:
print(f"❌ 测试失败: {str(e)}")
import traceback
traceback.print_exc()
# 仅在直接运行时执行测试
if __name__ == "__main__":
run_config_tests()
改成# E:\AI_System\core\config.py
import os
import json
import logging
from pathlib import Path
from typing import Dict, Any, Optional, Union
from dotenv import load_dotenv
import threading
import time
import hashlib
class CoreConfig:
"""核心配置管理器,支持多来源配置加载和热更新"""
def __init__(self,
config_path: Union[str, Path] = None,
env_prefix: str = "",
default_config: Dict = None):
"""
初始化配置管理器
Args:
config_path: 配置文件路径
env_prefix: 环境变量前缀
default_config: 默认配置字典
"""
# 初始化参数
self.config_path = Path(config_path) if config_path else None
self.env_prefix = env_prefix
self.default_config = default_config or {}
self._config = {}
self._last_hash = None
self._lock = threading.Lock()
# 设置日志器
self.logger = logging.getLogger('CoreConfig')
# 初始化配置
self._initialize()
def _initialize(self):
"""初始化配置"""
self.logger.info(f"📋 初始化配置管理器 | 环境前缀: {self.env_prefix} | 基础目录: {Path.cwd()}")
# 加载环境变量
self._load_environment()
# 加载配置文件
if self.config_path and self.config_path.exists():
self._load_config_file()
else:
self.logger.warning(f"⚠️ 配置文件不存在: {self.config_path}")
# 合并默认配置
self._merge_defaults()
# 创建必要目录
self._create_directories()
def _load_environment(self):
"""加载环境变量"""
self.logger.info("🔍 加载环境变量...")
# 加载系统环境变量
for key, value in os.environ.items():
if key.startswith(self.env_prefix):
config_key = key[len(self.env_prefix):].lower()
self._config[config_key] = value
# 加载 .env 文件
env_path = Path.cwd() / ".env"
if env_path.exists():
load_dotenv(env_path)
self.logger.info(f"✅ 已加载环境变量文件: {env_path}")
def _load_config_file(self):
"""加载配置文件"""
file_type = self.config_path.suffix.lower()
self.logger.info(f"📄 加载配置文件: {self.config_path} (类型: {file_type[1:]})")
try:
if file_type == '.json':
with open(self.config_path, 'r') as f:
config_data = json.load(f)
elif file_type == '.yaml' or file_type == '.yml':
import yaml
with open(self.config_path, 'r') as f:
config_data = yaml.safe_load(f)
elif file_type == '.ini':
import configparser
parser = configparser.ConfigParser()
parser.read(self.config_path)
config_data = {}
for section in parser.sections():
config_data[section] = dict(parser.items(section))
else:
raise ValueError(f"不支持的配置文件类型: {file_type}")
# 合并配置
self._deep_merge(self._config, config_data)
self._last_hash = self._calculate_hash()
# 记录加载的配置项
self._log_loaded_items(config_data)
except Exception as e:
self.logger.error(f"❌ 加载配置文件失败: {str(e)}")
def _log_loaded_items(self, config_data: Dict):
"""记录加载的配置项"""
for key, value in config_data.items():
if isinstance(value, dict):
self.logger.info(f" - 加载配置项: {key} = {{字典, 键数: {len(value)}}}")
elif isinstance(value, list):
self.logger.info(f" - 加载配置项: {key} = {{列表, 长度: {len(value)}}}")
elif isinstance(value, str) and len(value) > 20:
self.logger.info(f" - 加载配置项: {key} = {value[:10]}***{value[-10:]}")
elif key.lower().endswith('key') or key.lower().endswith('secret'):
self.logger.info(f" - 加载配置项: {key} = {value[:3]}***{value[-3:]}")
else:
self.logger.info(f" - 加载配置项: {key} = {value}")
def _merge_defaults(self):
"""合并默认配置"""
self.logger.info("🔄 合并默认配置...")
self._deep_merge(self._config, self.default_config)
self.logger.info(f"✅ 配置加载完成 | 条目数: {len(self._config)}")
def _deep_merge(self, target: Dict, source: Dict):
"""深度合并字典"""
for key, value in source.items():
if (key in target and isinstance(target[key], dict)
and isinstance(value, dict)):
self._deep_merge(target[key], value)
else:
target[key] = value
def _calculate_hash(self) -> str:
"""计算配置哈希值"""
return hashlib.md5(json.dumps(self._config, sort_keys=True).encode()).hexdigest()
def _create_directories(self):
"""创建配置中指定的目录"""
dir_keys = ['log_dir', 'cache_dir', 'model_dir', 'data_dir']
created_dirs = []
for key in dir_keys:
if key in self._config and isinstance(self._config[key], str):
path = Path(self._config[key])
if not path.exists():
path.mkdir(parents=True, exist_ok=True)
created_dirs.append(str(path))
if created_dirs:
self.logger.info(f"📁 创建目录: {', '.join(created_dirs)}")
def check_for_updates(self) -> bool:
"""检查配置是否有更新"""
if not self.config_path or not self.config_path.exists():
return False
with open(self.config_path, 'rb') as f:
current_hash = hashlib.md5(f.read()).hexdigest()
if current_hash != self._last_hash:
self.logger.info("🔄 检测到配置文件更改,重新加载配置...")
self._load_config_file()
self._create_directories()
return True
return False
# 添加属性访问支持
def __getattr__(self, name: str) -> Any:
"""支持点操作符访问配置项"""
if name in self._config:
return self._config[name]
# 支持嵌套属性访问 (例如 system_config.app.name)
parts = name.split('.')
current = self._config
for part in parts:
if part in current:
current = current[part]
else:
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
return current
def __getitem__(self, key: str) -> Any:
"""支持字典式访问配置项"""
return self._config[key]
def get(self, key: str, default: Any = None) -> Any:
"""安全获取配置值"""
parts = key.split('.')
current = self._config
for part in parts:
if part in current:
current = current[part]
else:
return default
return current
def set(self, key: str, value: Any):
"""设置配置值"""
parts = key.split('.')
current = self._config
for i, part in enumerate(parts):
if i == len(parts) - 1:
current[part] = value
else:
if part not in current:
current[part] = {}
current = current[part]
def save(self):
"""保存配置到文件"""
if not self.config_path:
self.logger.warning("⚠️ 没有配置文件路径,无法保存配置")
return
try:
with open(self.config_path, 'w') as f:
if self.config_path.suffix.lower() == '.json':
json.dump(self._config, f, indent=2)
elif self.config_path.suffix.lower() in ('.yaml', '.yml'):
import yaml
yaml.dump(self._config, f)
elif self.config_path.suffix.lower() == '.ini':
import configparser
parser = configparser.ConfigParser()
# 将字典转换为INI格式
# 注意:INI格式不支持嵌套结构,所以只保存顶级键值对
for key, value in self._config.items():
if isinstance(value, dict):
parser.add_section(key)
for subkey, subvalue in value.items():
parser.set(key, subkey, str(subvalue))
else:
parser['DEFAULT'][key] = str(value)
parser.write(f)
self._last_hash = self._calculate_hash()
self.logger.info(f"💾 配置已保存到: {self.config_path}")
except Exception as e:
self.logger.error(f"❌ 保存配置失败: {str(e)}")
def __str__(self) -> str:
"""返回配置的字符串表示"""
return json.dumps(self._config, indent=2)
def to_dict(self) -> Dict:
"""返回配置字典"""
return self._config.copy()
吗?
最新发布