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"))