Source code for mph.config

"""Manages configuration options."""

from __future__ import annotations

import os
import platform
from configparser import ConfigParser
from pathlib import Path
from logging import getLogger

from typing import overload, TypedDict


[docs] class Options(TypedDict): session: str caching: bool classkit: bool
options = { 'session': 'client-server', 'caching': False, 'classkit': False, } """Default values for configuration options.""" system = platform.system() log = getLogger(__package__) ########## # Access # ########## @overload def option(name: None, value: None) -> Options: ... @overload def option(name: str, value: None) -> str | bool | int | float: ... @overload def option(name: str, value: str | bool | int | float): ...
[docs] def option(name=None, value=None): """ Sets or returns the value of a configuration option. If called without arguments, returns all configuration options as a dictionary. Returns an option's value if only called with the option's `name`. Otherwise sets the option to the given `value`. """ if name is None: return options if name not in options: error = f'Configuration option "{name}" does not exist.' log.error(error) raise LookupError(error) if value is None: return options[name] else: options[name] = value
########### # Storage # ###########
[docs] def location() -> Path: """ Returns the default location of the configuration file. The folder returned by this function is platform-specific. It is inside the user's `AppData` folder on Windows, inside `.config` in the home directory on Linux, and in `Application Support` on macOS. """ if system == 'Windows': return Path(os.environ['APPDATA'])/'MPh' elif system == 'Linux': return Path.home()/'.config'/'MPh' elif system == 'Darwin': return Path.home()/'Library'/'Application Support'/'MPh' else: return Path.home()/'MPh'
[docs] def load(file: Path | str = None): """ Loads the configuration from the given `.ini` file. If `file` is not given, looks for a configuration file named `MPh.ini` in the current directory, or in the folder inside the user profile as returned by [`location()`](#location), or in this library's folder, in that order. If no such file is found, the hard-coded default values are used. """ if not file: folders = [Path.cwd(), location(), Path(__file__).parent] for folder in folders: file = folder/'MPh.ini' if file.exists(): break else: log.debug('Using default configuration.') return log.debug(f'Loading configuration from "{file}".') parser = ConfigParser(interpolation=None) parser.optionxform = str parser.read(file, encoding='UTF-8') section = 'config' if section not in parser.sections(): log.debug(f'Section [{section}] missing in configuration file.') return for (key, value) in options.items(): if key in parser[section]: if isinstance(value, bool): options[key] = parser.getboolean(section, key) elif isinstance(value, int): options[key] = parser.getint(section, key) elif isinstance(value, float): options[key] = parser.getfloat(section, key) else: options[key] = parser[section][key]
[docs] def save(file: Path | str = None): """ Saves the configuration in the given `.ini` file. If `file` is not given, saves the configuration in `MPh.ini` inside the default folder returned by [`location()`](#location). """ if not file: file = location()/'MPh.ini' else: file = Path(file) parser = ConfigParser(interpolation=None) parser.optionxform = str section = 'config' parser.add_section(section) for (key, value) in options.items(): parser[section][key] = str(value) file.parent.mkdir(exist_ok=True, parents=True) with file.open('w', encoding='UTF-8') as stream: parser.write(stream)
########## # Init # ########## # Load custom configuration at start-up. load()