Source code for aiida.manage.configuration

# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved.                     #
# This file is part of the AiiDA code.                                    #
#                                                                         #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida-core #
# For further information on the license, see the LICENSE.txt file        #
# For further information please visit http://www.aiida.net               #
###########################################################################
# pylint: disable=undefined-variable,wildcard-import,global-statement,redefined-outer-name,cyclic-import
"""Modules related to the configuration of an AiiDA instance."""
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import

from .config import *
from .options import *
from .profile import *

CONFIG = None
PROFILE = None
BACKEND_UUID = None  # This will be set to the UUID of the profile as soon as its corresponding backend is loaded

# This is used (and should be set to true) for the correct compilation of the documentation on readthedocs
IN_RT_DOC_MODE = False

__all__ = (
    config.__all__ + options.__all__ + profile.__all__ +
    ('get_config', 'get_config_option', 'load_profile', 'reset_config')
)


[docs]def load_profile(profile=None): """Load a profile. .. note:: if a profile is already loaded and no explicit profile is specified, nothing will be done :param profile: the name of the profile to load, by default will use the one marked as default in the config :type profile: str :return: the loaded `Profile` instance :rtype: :class:`~aiida.manage.configuration.Profile` :raises `aiida.common.exceptions.InvalidOperation`: if the backend of another profile has already been loaded """ from aiida.common import InvalidOperation from aiida.common.log import configure_logging global PROFILE global BACKEND_UUID # If a profile is loaded and the specified profile name is None or that of the currently loaded, do nothing if PROFILE and (profile is None or PROFILE.name is profile): return PROFILE profile = get_config().get_profile(profile) if BACKEND_UUID is not None and BACKEND_UUID != profile.uuid: # Once the switching of profiles with different backends becomes possible, the backend has to be reset properly raise InvalidOperation('cannot switch profile because backend of another profile is already loaded') # Set the global variable and make sure the repository is configured PROFILE = profile PROFILE.configure_repository() # Reconfigure the logging to make sure that profile specific logging configuration options are taken into account. # Note that we do not configure with `with_orm=True` because that will force the backend to be loaded. This should # instead be done lazily in `Manager._load_backend`. configure_logging() return PROFILE
def load_config(create=False): """Instantiate the Config object representing the configuration file of the current AiiDA instance. :param create: if True, will create the configuration file if it does not already exist :type create: bool :return: the config :rtype: :class:`~aiida.manage.configuration.config.Config` :raises aiida.common.MissingConfigurationError: if the configuration file could not be found and create=False """ import io import os from aiida.common import exceptions from aiida.common import json from .config import Config from .migrations import check_and_migrate_config from .settings import AIIDA_CONFIG_FOLDER, DEFAULT_CONFIG_FILE_NAME if IN_RT_DOC_MODE: # The following is a dummy config.json configuration that it is used for the # proper compilation of the documentation on readthedocs. from aiida.manage.external.postgres import DEFAULT_DBINFO return Config( '/dev/null', { 'default_profile': 'default', 'profiles': { 'default': { 'AIIDADB_ENGINE': 'postgresql_psycopg2', 'AIIDADB_BACKEND': 'django', 'AIIDADB_HOST': DEFAULT_DBINFO['host'], 'AIIDADB_PORT': DEFAULT_DBINFO['port'], 'AIIDADB_NAME': 'aiidadb', 'AIIDADB_PASS': '123', 'default_user_email': 'aiida@epfl.ch', 'TIMEZONE': 'Europe/Zurich', 'AIIDADB_REPOSITORY_URI': 'file:///tmp/repository', 'AIIDADB_USER': 'aiida' } } } ) filepath = os.path.join(AIIDA_CONFIG_FOLDER, DEFAULT_CONFIG_FILE_NAME) if not os.path.isfile(filepath): if not create: raise exceptions.MissingConfigurationError('configuration file {} does not exist'.format(filepath)) else: config_dictionary = {} else: try: with io.open(filepath, 'r', encoding='utf8') as handle: config_dictionary = json.load(handle) except ValueError: raise exceptions.ConfigurationError('configuration file {} contains invalid JSON'.format(filepath)) config = Config(filepath, check_and_migrate_config(config_dictionary)) config.store() return config def get_profile(): """Return the currently loaded profile. :return: the globally loaded `Profile` instance or `None` :rtype: :class:`~aiida.manage.configuration.Profile` """ global PROFILE return PROFILE def reset_profile(): """Reset the globally loaded profile. .. warning:: This is experimental functionality and should for now be used only internally. If the reset is unclean weird unknown side-effects may occur that end up corrupting or destroying data. """ global PROFILE global BACKEND_UUID PROFILE = None BACKEND_UUID = None
[docs]def reset_config(): """Reset the globally loaded config. .. warning:: This is experimental functionality and should for now be used only internally. If the reset is unclean weird unknown side-effects may occur that end up corrupting or destroying data. """ global CONFIG CONFIG = None
[docs]def get_config(create=False): """Return the current configuration. If the configuration has not been loaded yet, it will be loaded first and then returned. Note: this function should only be called by parts of the code that expect that a complete AiiDA instance exists, i.e. an AiiDA folder exists and contains a valid configuration file. :param create: if True, will create the configuration file if it does not already exist :type create: bool :return: the config :rtype: :class:`~aiida.manage.configuration.config.Config` :raises aiida.common.ConfigurationError: if the configuration file could not be found, read or deserialized """ global CONFIG if not CONFIG: CONFIG = load_config(create=create) return CONFIG
[docs]def get_config_option(option_name): """Return the value for the given configuration option. This function will attempt to load the value of the option as defined for the current profile or otherwise as defined configuration wide. If no configuration is yet loaded, this function will fall back on the default that may be defined for the option itself. This is useful for options that need to be defined at loading time of AiiDA when no configuration is yet loaded or may not even yet exist. In cases where one expects a profile to be loaded, preference should be given to retrieving the option through the Config instance and its `get_option` method. :param option_name: the name of the configuration option :type option_name: str :return: option value as specified for the profile/configuration if loaded, otherwise option default """ from aiida.common import exceptions option = options.get_option(option_name) try: config = get_config(create=True) except exceptions.ConfigurationError: value = option.default if option.default is not options.NO_DEFAULT else None else: if config.current_profile: # Try to get the option for the profile, but do not return the option default value_profile = config.get_option(option_name, scope=config.current_profile.name, default=False) else: value_profile = None # Value is the profile value if defined or otherwise the global value, which will be None if not set value = value_profile if value_profile else config.get_option(option_name) return value