Source code for AutoArchive._infrastructure.configuration._configuration_factory

# _configuration_factory.py
#
# Project: AutoArchive
# License: GNU GPLv3
#
# Copyright (C) 2003 - 2025 Róbert Čerňanský



__all__ = ["ConfigurationFactory"]



import os

from AutoArchive._infrastructure.utils import OsPaths, ErrorHandling
from . import Constants as ConfigurationConstants, ArchiverTypes, ConfigurationBase
from . import Options, OptionsUtils
from ._cmdline_arguments_processor import _CmdlineArgumentsProcessor
from ._config_file_processor import _ConfigFileProcessor
from ._configuration import _Configuration



# SMELL: Creation of archive specifications directory should perhaps go to Archiving?
[docs] class ConfigurationFactory: """Support of configuration options and a persistent storage. During :meth:`makeConfiguration` call it creates :class:`.ConfigurationBase` implementation instance. It also creates :term:`user configuration directory` and :term:`archive specifications directory` if either of them does not exist. The :class:`.ConfigurationBase`-like class provides access to configuration options. During the :meth:`makeConfiguration` call it is instantiated and populated from :attr:`.AppEnvironment.commandLineOptions` and from system- and user-configuration files. The :term:`system configuration file` is assembled from :meth:`.OsPaths.getSystemConfigDir()` and :attr:`.Constants.CONFIG_FILE_NAME`. The :term:`user configuration file` location is determined by values of :attr:`.Options.USER_CONFIG_FILE` and :attr:`.Options.USER_CONFIG_DIR` options. The :term:`user configuration directory` is automatically created if it does not exists. The format of configuration files is defined by the standard :mod:`configparser` module (without interpolation). The :class:`.ConfigurationBase` implementation instance is populated in the way that same options specified in multiple sources overrides each other so that the order of precedence from highest to lowest is following: command-line, user configuration file, system configuration file. However the implementation recognizes certain types of options that does not follow this rule (see :meth:`.ConfigurationBase.__getitem__()`). Some of the options, if not specified in neither of possible sources, has some predefined default value. The list of these options with their predefined value follows: * :attr:`.Options.ARCHIVER`\ : :attr:`.ArchiverTypes.TarGz` * :attr:`.Options.DEST_DIR`\ : ``os.curdir`` * :attr:`.Options.RESTART_AFTER_LEVEL`\ : 10 * :attr:`.Options.ARCHIVE_SPECS_DIR`\ : :attr:`.Options.USER_CONFIG_DIR` + "archive_specs" * :attr:`.Options.USER_CONFIG_FILE`\ : :attr:`.Options.USER_CONFIG_DIR` + :attr:`.Constants.CONFIG_FILE_NAME` * :attr:`.Options.USER_CONFIG_DIR`\ : result of :meth:`.OsPaths.getUserConfigDir()` * :attr:`.Options.NUMBER_OF_OLD_BACKUPS`\ : 1 * options of type ``bool``\ : ``False``""" # system configuration file - full path __SYSTEM_CONFIG_FILE = os.path.join(OsPaths.getSystemConfigDir(), ConfigurationConstants.CONFIG_FILE_NAME) # user configuration directory __FACTORY_USER_CONFIG_DIR = OsPaths.getUserConfigDir()
[docs] @classmethod def makeConfiguration(cls, appEnvironment, systemConfigFile = __SYSTEM_CONFIG_FILE) -> ConfigurationBase: """Creates and populates configuration. :param appEnvironment: Application environment. :type appEnvironment: :class:`.AppEnvironment` :param systemConfigFile: Path to the system configuration file. :type systemConfigFile: `str` :rtype: :class:`.ConfigurationBase`""" return cls.__createAndPopulateConfig(appEnvironment, systemConfigFile)
@classmethod def __createAndPopulateConfig(cls, appEnvironment, systemConfigFile): cmdlineArgumentsProcessor = _CmdlineArgumentsProcessor(appEnvironment.commandLineOptions) cmdlineUserConfigDir, cmdlineUserConfigFile = cls.__getUserPathsFromCmdline( cmdlineArgumentsProcessor, appEnvironment) # create _Configuration which will be registered to Component Accessor configuration = _Configuration() # populate configuration with config. file options configFileProcessor = _ConfigFileProcessor( appEnvironment, systemConfigFile, cls.__FACTORY_USER_CONFIG_DIR, lambda usrDir: os.path.join(usrDir, ConfigurationConstants.CONFIG_FILE_NAME), cmdlineUserConfigDir, cmdlineUserConfigFile) configFileProcessor.populateConfiguration(configuration) # populate configuration with command line options cmdlineArgumentsProcessor.populateConfiguration(configuration) cls.__populateWithDefaults(configuration) return configuration @staticmethod def __getUserPathsFromCmdline( cmdlineArgumentsProcessor: _CmdlineArgumentsProcessor, appEnvironment) -> tuple[str, str]: # create temporary _Configuration and populate it with command line options cmdlineConfiguration = _Configuration() cmdlineArgumentsProcessor.populateConfiguration(cmdlineConfiguration) # determine and create user config directory if it does not exist cmdlineUserConfigDir: str = cmdlineConfiguration[Options.USER_CONFIG_DIR] # get user config file passed as a command-line option, if any and show error if it does not exist cmdlineUserConfigFile: str = cmdlineConfiguration[Options.USER_CONFIG_FILE] if cmdlineUserConfigFile: if not os.path.isfile(cmdlineUserConfigFile): ErrorHandling.printError(str.format("Configuration file \"{}\" does not exists.", cmdlineUserConfigFile), appEnvironment.executableName) return cmdlineUserConfigDir, cmdlineUserConfigFile @classmethod def __populateWithDefaults(cls, configuration): configuration._setDefault(Options.ARCHIVER, OptionsUtils.archiverTypeToStr(ArchiverTypes.TarGz)) configuration._setDefault(Options.DEST_DIR, os.curdir) configuration._setDefault(Options.RESTART_AFTER_LEVEL, 10) configuration._setDefault(Options.NUMBER_OF_OLD_BACKUPS, 1) configuration._setDefault(Options.USER_CONFIG_DIR, cls.__FACTORY_USER_CONFIG_DIR) configuration._setDefault(Options.USER_CONFIG_FILE, os.path.join( configuration[Options.USER_CONFIG_DIR], ConfigurationConstants.CONFIG_FILE_NAME)) configuration._setDefault(Options.ARCHIVE_SPECS_DIR, os.path.join( configuration[Options.USER_CONFIG_DIR], "archive_specs"))