Files
config-converter-cli/config_converter/converters/ini_converter.py
7000pctAUTO cd2cc703c9
Some checks failed
CI / test (push) Has been cancelled
fix: resolve CI linting and type checking issues
2026-02-04 22:07:55 +00:00

133 lines
5.4 KiB
Python

"""INI format converter."""
from configparser import ConfigParser, RawConfigParser
from pathlib import Path
from typing import Any, Dict, Union
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: Union[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: Union[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:
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