Исходный код EJIO.utils.serialization.implementations.ini_serializer
## /utils/serialization/implementations/ini_serializer.py
import configparser
import io
from os.path import exists
from typing import Any
from ..serializer import Serializer
from ..serializer_registry import register_serializer
from ..err import SerializationError
__all__ = [
'INISerializer'
]
[документация]
@register_serializer('ini')
class INISerializer(Serializer):
"""
Реализация интерфейса Serializer для конфигурационного формата INI.
Канонично использует статические методы и современный синтаксис дженериков Python 3.12+.
"""
__slots__ = ()
[документация]
@staticmethod
def serialize[T](data: dict[str, T], /, **kwargs: Any) -> str:
"""
Преобразовать словарь в INI-строку.
Если передан плоский словарь, данные автоматически упаковываются в секцию DEFAULT.
:param data: Сериализуемый словарь.
:param kwargs: Ключевые параметры для сереализатора.
:return: INI-строка.
:raises SerializationError: При ошибках сериализации.
"""
try:
config = configparser.ConfigParser()
# Анализируем структуру: есть ли вложенные словари (секции)
if not any(isinstance(v, dict) for v in data.values()):
# Плоский словарь -> пишем в DEFAULT
config['DEFAULT'] = {str(k): str(v) for k, v in data.items()}
else:
# Структурированный словарь -> распределяем по секциям
for section, content in data.items():
if isinstance(content, dict):
config[str(section)] = {str(k): str(v) for k, v in content.items()}
else:
if 'DEFAULT' not in config:
config['DEFAULT'] = {}
config['DEFAULT'][str(section)] = str(content)
output = io.StringIO()
config.write(output, **kwargs)
return output.getvalue()
except Exception as e:
raise SerializationError("Serialization to ini failed") from e
[документация]
@staticmethod
def deserialize[T](data: str, /, **kwargs: Any) -> dict[str, T]:
"""
Преобразовать INI-строку обратно в словарь.
Если задействована только дефолтная секция, возвращает её в плоском виде.
:param data: INI-строка.
:param kwargs: Ключевые параметры для сереализатора.
:return: Десериализованный словарь.
:raises SerializationError: При ошибках десериализации.
"""
try:
config = configparser.ConfigParser()
config.read_string(data, **kwargs)
result: dict[str, Any] = {}
# Извлекаем глобальные дефолтные параметры
if dict(config.defaults()):
result['DEFAULT'] = dict(config.defaults())
# Извлекаем именованные секции
for section in config.sections():
result[section] = dict(config[section])
# Для плоских конфигураций возвращаем содержимое DEFAULT напрямую
if len(result) == 1 and 'DEFAULT' in result:
return result['DEFAULT'] # type: ignore[return-value]
return result # type: ignore[return-value]
except configparser.Error as e:
raise SerializationError("Deserialization from ini failed") from e
[документация]
@staticmethod
def save[T](data: dict[str, T], filename: str, /, **kwargs: Any) -> None:
"""
Сохранить словарь в INI-файл.
:param data: Сериализуемый словарь.
:param filename: Путь к файлу для сохранения.
:param kwargs: Ключевые параметры для сереализатора.
:raises SerializationError: При ошибках сериализации.
"""
try:
ini_string = INISerializer.serialize(data, **kwargs)
with open(filename, 'w', encoding='utf-8') as f:
f.write(ini_string)
except (IOError, OSError, SerializationError) as e:
raise SerializationError("Failed to serialize data to ini file") from e
[документация]
@staticmethod
def load[T](filename: str, /, **kwargs: Any) -> dict[str, T]:
"""
Загрузить словарь из INI-файла.
:param filename: Путь к файлу для загрузки.
:param kwargs: Ключевые параметры для сереализатора.
:return: Словарь, полученный из файла.
:raises SerializationError: При ошибках десериализации.
"""
try:
if not exists(filename):
raise FileNotFoundError(f"File {filename} not found")
with open(filename, 'r', encoding='utf-8') as f:
data = f.read()
return INISerializer.deserialize(data, **kwargs)
except (IOError, OSError, FileNotFoundError, SerializationError) as e:
raise SerializationError("Failed to deserialize data from ini file") from e