Исходный код 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