Files
EDMarketConnector/config/linux.py
T

154 lines
5.0 KiB
Python

"""
linux.py - Linux config implementation.
Copyright (c) EDCD, All Rights Reserved
Licensed under the GNU General Public License v2 or later.
See LICENSE file.
"""
from __future__ import annotations
import os
import sys
from config import config_logger
if sys.platform != "linux":
raise OSError("This file is for Linux only.")
import pathlib
from configparser import ConfigParser
import datetime
import tomli_w
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
from . import Config
class LinuxConfigMinimal:
"""Minimal Linux config for exporting pre-6.0 config values to TOML."""
SECTION = "config"
def __init__(self, filename: str | None = None) -> None:
# Determine config path
xdg_config_home = pathlib.Path(
os.getenv("XDG_CONFIG_HOME", "~/.config")
).expanduser()
self.filename = (
pathlib.Path(filename)
if filename
else xdg_config_home / "EDMarketConnector" / "EDMarketConnector.ini"
)
if not pathlib.Path(self.filename).is_file():
raise FileNotFoundError
self.config = ConfigParser(comment_prefixes=('#',), interpolation=None)
try:
read_files = self.config.read(self.filename)
config_logger.debug("ConfigParser.read returned: %s", read_files)
except Exception as e:
config_logger.exception("Failed to read INI file %s: %s", self.filename, e)
raise
__unescape_lut = {'\\': '\\', 'n': '\n', ';': ';', 'r': '\r', '#': '#'}
def __unescape(self, s: str) -> str:
"""
Unescape a string.
:param s: str - The string to unescape.
:return: str - The unescaped string.
"""
out: list[str] = []
i = 0
while i < len(s):
c = s[i]
if c != '\\':
out.append(c)
i += 1
continue
# We have a backslash, check what it's escaping
if i == len(s) - 1:
raise ValueError('Escaped string has unescaped trailer')
unescaped = self.__unescape_lut.get(s[i + 1])
if unescaped is None:
raise ValueError(f'Unknown escape: \\{s[i+1]}')
out.append(unescaped)
i += 2
return "".join(out)
def _get_settings_dict(self) -> dict[str, Any]: # noqa: CCR001
"""
Return all keys/values from SECTION as a dict of strings or lists.
This decodes the historical encoding where lists are encoded as
newline-separated values separated by ';'
"""
result: dict[str, Any] = {}
for key in self.config[self.SECTION]:
raw = self.config[self.SECTION][key]
if raw is None:
continue
# If the value contains newline(s), check for trailing ';' sentinel
if "\n" in raw:
parts = raw.split("\n")
if parts and parts[-1].strip() == ";":
items = [self.__unescape(p.strip()) for p in parts[:-1]]
result[key] = items
config_logger.debug("Decoded list for key '%s' with %d items", key, len(items))
continue
else:
# Not a valid encoded list; fall back to unescaped string
try:
result[key] = self.__unescape(raw)
except Exception:
result[key] = raw
continue
# Single value: unescape and return
try:
result[key] = self.__unescape(raw)
except Exception:
result[key] = raw
return result
def write_to_toml(self, toml_path: str) -> None:
"""Dump existing config to TOML file."""
config_logger.debug("Generating New Config File")
config_data: dict[str, Any] = {
"generated": datetime.datetime.now(datetime.timezone.utc).isoformat(),
"source": "linux_ini",
"section": self.SECTION,
"settings": self._get_settings_dict(),
}
path_obj = pathlib.Path(toml_path)
path_obj.parent.mkdir(parents=True, exist_ok=True)
config_logger.debug("Writing Config File to %s", path_obj)
with path_obj.open("wb") as f:
tomli_w.dump(config_data, f)
def close(self) -> None:
"""Release resources (stub)."""
del self.config
def linux_helper(config: Config) -> Config:
"""Set Environment Specific Variables for Linux Config."""
config_logger.debug("Linux environment detected. Setting platform-specific variables.")
config.respath_path = pathlib.Path(__file__).parent.parent
config.internal_plugin_dir_path = config.respath_path / "plugins"
config.default_journal_dir_path = None # type: ignore
if (outdir := config.get_str("outdir")) is None or not pathlib.Path(
outdir
).is_dir():
config.set("outdir", config.home)
return config