AutoArchive._infrastructure.configuration._config_file_processor — AutoArchive Developer Manual (ver. 2.0.6)

# Source code for AutoArchive._infrastructure.configuration._config_file_processor

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

    """:class:`_ConfigFileProcessor` class."""

    __all__ = ["_ConfigFileProcessor"]

    # {{{ INCLUDES

    import configparser

    from AutoArchive._infrastructure.utils import ErrorHandling
    from . import Options

    # }}} INCLUDES

    # {{{ CLASSES

    class _ConfigFileProcessor:
        """Processes configuration files and populates :class:`.IConfiguration` instance.

        :param appEnvironment: :class:`.AppEnvironment` instance.
        :type appEnvironment: :class:`.AppEnvironment`
        :param systemConfigFile: Path to the system configuration file.
        :type systemConfigFile: `str`
        :param factoryUserConfigDir: Pre-defined path to the :term:`user configuration directory`.
        :type factoryUserConfigDir: ``str``
        :param factoryUserConfigFileProvider: Function that returns path to the :term:`user configuration file` based on
           the passed configuration directory.
        :type factoryUserConfigFileProvider: ``function{str, =>str}``
        :param userConfigDir: Path to the user configuration directory.
        :type userConfigDir: ``str``
        :param userConfigFile: Path to the user configuration file.
        :type userConfigFile: ``str``"""

        __SECTION_GENERAL = "General"
        __SECTION_ARCHIVE = "Archive"

        # options not suitable for a configuration file
        __INVALID_OPTIONS = frozenset(str(opt)
                                      for opt in (Options.LEVEL,
                                                  Options.ALL,
                                                  Options.NO_INCREMENTAL,
                                                  Options.NO_RESTARTING,
                                                  Options.NO_REMOVE_OBSOLETE_BACKUPS,
                                                  Options.NO_KEEP_OLD_BACKUPS,
                                                  Options.NO_ALL,
                                                  Options.NO_OVERWRITE_AT_START))

        # options not suitable for the system configuration file
        __INVALID_OPTIONS_SYSTEM = frozenset()

        # options not suitable for the user configuration file
        __INVALID_OPTIONS_USER = frozenset(str(opt)
                                           for opt in (Options.USER_CONFIG_FILE,
                                                       Options.USER_CONFIG_DIR))

        def __init__(self, appEnvironment, systemConfigFile, factoryUserConfigDir, factoryUserConfigFileProvider,
                     userConfigDir = None, userConfigFile = None):
            self.__appEnvironment = appEnvironment
            self.__systemConfigFile = systemConfigFile
            self.__factoryUserConfigDir = factoryUserConfigDir
            self.__factoryUserConfigFileProvider = factoryUserConfigFileProvider
            self.__userConfigDir = userConfigDir
            self.__userConfigFile = userConfigFile

        def populateConfiguration(self, configuration):
            """Populates ``configuration`` with options.

            :param configuration: Configuration that should be populated.
            :type configuration: :class:`._Configuration`"""

            supportedSections = {self.__SECTION_GENERAL, self.__SECTION_ARCHIVE}

            # process system configuration file
            self.__processConfigFile(
                self.__systemConfigFile, configuration, supportedSections,
                self.__INVALID_OPTIONS | self.__INVALID_OPTIONS_SYSTEM)

            # process user configuration file
            userConfigDir = self.__userConfigDir or configuration[Options.USER_CONFIG_DIR] or self.__factoryUserConfigDir

            userConfigFile = self.__userConfigFile or configuration[Options.USER_CONFIG_FILE] or \
                    self.__factoryUserConfigFileProvider(userConfigDir)

            self.__processConfigFile(
                userConfigFile, configuration, supportedSections, self.__INVALID_OPTIONS | self.__INVALID_OPTIONS_USER)

        def __processConfigFile(self, configFile, configuration, supportedSections, invalidOptions):
            configParser = configparser.ConfigParser(interpolation = None)
            try:
                configParser.read(configFile)
            except (configparser.MissingSectionHeaderError, configparser.ParsingError) as ex:
                ErrorHandling.fatalExit(ex)
            except UnicodeDecodeError as ex:
                ErrorHandling.fatalExit(str.format("Unable to parse file \"{}\" [{}].", configFile, ex))

            if len(supportedSections.union(configParser.sections())) > len(supportedSections):
                ErrorHandling.fatalExit(str.format(
                        "One or more invalid sections found in the configuration file \"{}\".", configFile),
                                 self.__appEnvironment.executableName)

            self.__addOptionsFromSection(self.__SECTION_GENERAL, configuration, configParser, configFile, invalidOptions)
            self.__addOptionsFromSection(self.__SECTION_ARCHIVE, configuration, configParser, configFile, invalidOptions)

        def __addOptionsFromSection(self, section, configuration, configParser, configFile, invalidOptions):
            if configParser.has_section(section):
                for configOption in configParser.options(section):
                    try:
                        if configOption in invalidOptions:
                            raise KeyError("Not suitable for a config. file")
                        value = configParser.get(section, configOption)
                        configuration._addOrReplaceOption(configOption, value)
                    except KeyError:
                        ErrorHandling.fatalExit(str.format(
                                "Invalid option \"{}\" in the configuration file \"{}\".", configOption, configFile),
                                         self.__appEnvironment.executableName)
                    except ValueError:
                        ErrorHandling.fatalExit(str.format(
                                "Wrong value \"{}\" of the option \"{}\" in the configuration file \"{}\".",
                                value, configOption, configFile), self.__appEnvironment.executableName)

    # }}} CLASSES