From 8ff6169518fc4c445a3c1fa7ccb7cc29ea7e0bde Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Wed, 4 Feb 2026 21:54:22 +0000 Subject: [PATCH] Add source code files --- config_converter/converters/ini_converter.py | 135 +++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 config_converter/converters/ini_converter.py diff --git a/config_converter/converters/ini_converter.py b/config_converter/converters/ini_converter.py new file mode 100644 index 0000000..0996d68 --- /dev/null +++ b/config_converter/converters/ini_converter.py @@ -0,0 +1,135 @@ +"""INI format converter.""" + +import re +from configparser import ConfigParser, RawConfigParser +from pathlib import Path +from typing import Any, Dict, Optional + +from config_converter.converters.base import BaseConverter, ConversionError + + +def flatten_dict(data: Dict[str, Any], parent_key: str = "", sep: str = ".") -> Dict[str, Any]: + """Flatten nested dictionary for INI output.""" + items: Dict[str, Any] = {} + for key, value in data.items(): + new_key = f"{parent_key}{sep}{key}" if parent_key else key + if isinstance(value, dict): + items.update(flatten_dict(value, new_key, sep)) + else: + items[new_key] = value + return items + + +def unflatten_dict(data: Dict[str, Any], sep: str = ".") -> Dict[str, Any]: + """Unflatten flat dictionary for INI input.""" + result: Dict[str, Any] = {} + for key, value in data.items(): + parts = key.split(sep) + target = result + for part in parts[:-1]: + if part not in target: + target[part] = {} + target = target[part] + target[parts[-1]] = value + return result + + +class IniConverter(BaseConverter): + """Converter for INI configuration files.""" + + FORMAT_NAME = "ini" + FILE_EXTENSIONS = ["ini", "cfg"] + + def read(self, source: str | Path) -> Dict[str, Any]: + """Read and parse an INI configuration file.""" + try: + config = RawConfigParser(allow_no_value=True) + config.read(source, encoding="utf-8") + result: Dict[str, Any] = {} + for section in config.sections(): + section_dict: Dict[str, Any] = {} + for key, value in config.items(section): + if value: + try: + section_dict[key] = int(value) + except ValueError: + try: + section_dict[key] = float(value) + except ValueError: + if value.lower() in ("true", "yes", "on"): + section_dict[key] = True + elif value.lower() in ("false", "no", "off"): + section_dict[key] = False + else: + section_dict[key] = value + else: + section_dict[key] = None + result[section] = section_dict + return result + except Exception as e: + raise ConversionError(f"Invalid INI in {source}: {e}") from e + + def write(self, data: Dict[str, Any], target: str | Path) -> None: + """Write configuration data to an INI file.""" + try: + config = ConfigParser() + for section, section_data in data.items(): + if isinstance(section_data, dict): + config[section] = {} + for key, value in section_data.items(): + config[section][key] = str(value) if value is not None else "" + else: + config["DEFAULT"] = {} + config["DEFAULT"]["settings"] = str(section_data) + with open(target, "w", encoding="utf-8") as f: + config.write(f) + except (OSError, TypeError) as e: + raise ConversionError(f"Failed to write INI to {target}: {e}") from e + + def parse(self, content: str) -> Dict[str, Any]: + """Parse INI content from a string.""" + try: + from io import StringIO + + config = RawConfigParser(allow_no_value=True) + config.read_string(content) + result: Dict[str, Any] = {} + for section in config.sections(): + section_dict: Dict[str, Any] = {} + for key, value in config.items(section): + if value: + try: + section_dict[key] = int(value) + except ValueError: + try: + section_dict[key] = float(value) + except ValueError: + if value.lower() in ("true", "yes", "on"): + section_dict[key] = True + elif value.lower() in ("false", "no", "off"): + section_dict[key] = False + else: + section_dict[key] = value + else: + section_dict[key] = None + result[section] = section_dict + return result + except Exception as e: + raise ConversionError(f"Invalid INI content: {e}") from e + + def format(self, data: Dict[str, Any]) -> str: + """Format configuration data to an INI string.""" + try: + config = ConfigParser() + for section, section_data in data.items(): + if isinstance(section_data, dict): + config[section] = {} + for key, value in section_data.items(): + config[section][key] = str(value) if value is not None else "" + from io import StringIO + + output = StringIO() + config.write(output) + return output.getvalue() + except (TypeError, ValueError) as e: + raise ConversionError(f"Failed to format data as INI: {e}") from e