Source code for plumbum.cli.config

from __future__ import annotations

__lazy_modules__ = {"configparser", "contextlib"}

import contextlib
from abc import ABC, abstractmethod
from configparser import ConfigParser, NoOptionError, NoSectionError
from typing import TYPE_CHECKING, Any

from plumbum import local

if TYPE_CHECKING:
    from plumbum._compat.typing import Self
    from plumbum.path.local import LocalPath


[docs] class ConfigBase(ABC): """Base class for Config parsers. :param filename: The file to use The ``with`` statement can be used to automatically try to read on entering and write if changed on exiting. Otherwise, use ``.read`` and ``.write`` as needed. Set and get the options using ``[]`` syntax. Usage: with Config("~/.myprog_rc") as conf: value = conf.get("option", "default") value2 = conf["option"] # shortcut for default=None """ __slots__ = ["changed", "filename"] def __init__(self, filename: str): self.filename: LocalPath = local.path(filename) self.changed = False def __enter__(self) -> Self: with contextlib.suppress(FileNotFoundError): self.read() return self def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None: if self.changed: self.write()
[docs] @abstractmethod def read(self) -> None: """Read in the linked file"""
[docs] @abstractmethod def write(self) -> None: """Write out the linked file""" self.changed = False
@abstractmethod def _get(self, option: str) -> Any: """Internal get function for subclasses""" @abstractmethod def _set(self, option: str, value: Any) -> str: """Internal set function for subclasses. Must return the value that was set."""
[docs] def get(self, option: str, default: Any = None) -> Any: "Get an item from the store, returns default if fails" try: return self._get(option) except KeyError: self.changed = True return self._set(option, default)
[docs] def set(self, option: str, value: Any) -> None: """Set an item, mark this object as changed""" self.changed = True self._set(option, value)
def __getitem__(self, option: str) -> Any: return self._get(option) def __setitem__(self, option: str, value: Any) -> None: return self.set(option, value)
[docs] class ConfigINI(ConfigBase): DEFAULT_SECTION = "DEFAULT" __slots__ = ("parser",) def __init__(self, filename: str): super().__init__(filename) self.parser = ConfigParser()
[docs] def read(self) -> None: self.parser.read(self.filename)
[docs] def write(self) -> None: with open(self.filename, "w", encoding="utf-8") as f: self.parser.write(f) super().write()
@classmethod def _sec_opt(cls, option: str) -> tuple[str, str]: if "." not in option: sec = cls.DEFAULT_SECTION else: sec, option = option.split(".", 1) return sec, option def _get(self, option: str) -> Any: sec, option = self._sec_opt(option) try: return self.parser.get(sec, option) except (NoSectionError, NoOptionError): raise KeyError(f"{sec}:{option}") from None def _set(self, option: str, value: Any) -> str: sec, option = self._sec_opt(option) try: self.parser.set(sec, option, str(value)) except NoSectionError: self.parser.add_section(sec) self.parser.set(sec, option, str(value)) return str(value)
Config = ConfigINI __all__ = [ "Config", "ConfigBase", "ConfigINI", ] def __dir__() -> list[str]: return list(__all__)