278 lines
8.8 KiB
Python
278 lines
8.8 KiB
Python
import os
|
||
import configparser
|
||
from typing import Dict, Any, Optional, List
|
||
|
||
|
||
class IniHandlerError(Exception):
|
||
"""INI处理器异常基类"""
|
||
def __init__(self, message: str, file_path: str = None):
|
||
self.file_path = file_path
|
||
if file_path:
|
||
message = f"{message} (文件: {file_path})"
|
||
super().__init__(message)
|
||
|
||
class IniHandler:
|
||
"""
|
||
INI文件操作处理器(基于文件路径的缓存)
|
||
"""
|
||
|
||
_instances = {} # 按文件路径缓存的实例
|
||
|
||
def __new__(cls, file_path: str):
|
||
if file_path not in cls._instances:
|
||
instance = super().__new__(cls)
|
||
instance._file_path = file_path
|
||
instance._config = None # 单个文件的配置对象
|
||
cls._instances[file_path] = instance
|
||
return cls._instances[file_path]
|
||
|
||
def __init__(self, file_path: str):
|
||
"""
|
||
初始化INI处理器
|
||
|
||
Args:
|
||
file_path: INI文件路径
|
||
"""
|
||
# 文件路径已经在__new__中设置,这里不需要重复设置
|
||
pass
|
||
|
||
def load_config(self) -> configparser.ConfigParser:
|
||
"""
|
||
加载并缓存INI配置文件
|
||
"""
|
||
if self._config is not None:
|
||
return self._config
|
||
|
||
if not os.path.exists(self._file_path):
|
||
raise IniHandlerError("INI文件不存在", self._file_path)
|
||
|
||
try:
|
||
config = configparser.ConfigParser()
|
||
config.read(self._file_path, encoding='utf-8')
|
||
self._config = config
|
||
return config
|
||
except Exception as e:
|
||
raise IniHandlerError(f"读取INI文件失败: {e}", self._file_path)
|
||
|
||
|
||
def get_value(self, section: str, option: str) -> Any:
|
||
"""
|
||
获取INI文件中的值
|
||
|
||
Args:
|
||
section: 节名
|
||
option: 选项名
|
||
default: 默认值
|
||
|
||
Returns:
|
||
获取的值,如果不存在则返回默认值
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
if config.has_section(section) and config.has_option(section, option):
|
||
return config.get(section, option)
|
||
except IniHandlerError:
|
||
# 文件不存在或读取失败时返回默认值
|
||
return default
|
||
except Exception as e:
|
||
raise IniHandlerError(f"获取INI值失败: {e}", self._file_path)
|
||
|
||
def get_int_value(self, section: str, option: str) -> int:
|
||
"""
|
||
获取INI文件中的整数值
|
||
|
||
Args:
|
||
section: 节名
|
||
option: 选项名
|
||
default: 默认值
|
||
|
||
Returns:
|
||
获取的整数值,如果不存在或转换失败则返回默认值
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
if config.has_section(section) and config.has_option(section, option):
|
||
return config.getint(section, option)
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"获取INI整数值失败: {e}", self._file_path)
|
||
|
||
def get_float_value(self, section: str, option: str) -> float:
|
||
"""
|
||
获取INI文件中的浮点数值
|
||
Args:
|
||
section: 节名
|
||
option: 选项名
|
||
default: 默认值
|
||
|
||
Returns:
|
||
获取的浮点数值,如果不存在或转换失败则返回默认值
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
if config.has_section(section) and config.has_option(section, option):
|
||
return config.getfloat(section, option)
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"获取INI浮点数值失败: {e}", self._file_path)
|
||
|
||
def get_boolean_value(self, section: str, option: str) -> bool:
|
||
"""
|
||
获取INI文件中的布尔值
|
||
Args:
|
||
section: 节名
|
||
option: 选项名
|
||
default: 默认值
|
||
|
||
Returns:
|
||
获取的布尔值,如果不存在或转换失败则返回默认值
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
if config.has_section(section) and config.has_option(section, option):
|
||
return config.getboolean(section, option)
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"获取INI布尔值失败: {e}", self._file_path)
|
||
|
||
|
||
def get_section(self, section: str) -> Dict[str, str]:
|
||
"""
|
||
获取INI文件中的整个节
|
||
Args:
|
||
section: 节名
|
||
Returns:
|
||
节中所有键值对的字典,如果节不存在则返回空字典
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
if config.has_section(section):
|
||
return dict(config[section])
|
||
return {}
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"获取INI节失败: {e}", self._file_path)
|
||
|
||
def get_sections(self) -> List[str]:
|
||
"""
|
||
获取INI文件中的所有节名
|
||
Returns:
|
||
节名列表
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
return config.sections()
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"获取INI节名列表失败: {e}", self._file_path)
|
||
|
||
def remove_section(self, section: str) -> None:
|
||
"""
|
||
移除INI文件中的节
|
||
Args:
|
||
section: 节名
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
if config.has_section(section):
|
||
config.remove_section(section)
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"移除INI节失败: {e}", self._file_path)
|
||
|
||
def remove_option(self, section: str, option: str) -> None:
|
||
"""
|
||
移除INI文件中的选项
|
||
Args:
|
||
section: 节名
|
||
option: 选项名
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
if config.has_section(section) and config.has_option(section, option):
|
||
config.remove_option(section, option)
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"移除INI选项失败: {e}", self._file_path)
|
||
|
||
def has_section(self, section: str) -> bool:
|
||
"""
|
||
检查INI文件中是否存在指定的节
|
||
|
||
Args:
|
||
section: 节名
|
||
|
||
Returns:
|
||
bool: 是否存在
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
return config.has_section(section)
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"检查INI节失败: {e}", self._file_path)
|
||
|
||
def has_option(self, section: str, option: str) -> bool:
|
||
"""
|
||
检查INI文件中是否存在指定的选项
|
||
|
||
Args:
|
||
section: 节名
|
||
option: 选项名
|
||
Returns:
|
||
bool: 是否存在
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
return config.has_option(section, option)
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"检查INI选项失败: {e}", self._file_path)
|
||
|
||
def write_ini_file(self) -> bool:
|
||
"""
|
||
写入INI文件(持久化,写入前先set_value,set_value设置1或多个值后调用此方法)
|
||
Returns:
|
||
bool: 是否写入成功
|
||
"""
|
||
try:
|
||
# 确保目录存在
|
||
dir_path = os.path.dirname(self._file_path)
|
||
if dir_path and not os.path.exists(dir_path):
|
||
os.makedirs(dir_path)
|
||
# 写入文件
|
||
with open(self._file_path, 'w', encoding='utf-8') as f:
|
||
self._config.write(f)
|
||
return True
|
||
except Exception as e:
|
||
raise IniHandlerError(f"写入INI文件失败: {e}", self._file_path)
|
||
|
||
def set_value(self, section: str, option: str, value: Any) -> None:
|
||
"""
|
||
设置INI文件中的值
|
||
|
||
Args:
|
||
section: 节名
|
||
option: 选项名
|
||
value: 要设置的值
|
||
"""
|
||
try:
|
||
config = self.load_config()
|
||
if not config.has_section(section):
|
||
config.add_section(section)
|
||
config.set(section, option, str(value))
|
||
except IniHandlerError:
|
||
raise
|
||
except Exception as e:
|
||
raise IniHandlerError(f"设置INI值失败: {e}", self._file_path)
|
||
|
||
|