Skip to content

Commit

Permalink
Remove READTHEDOCS specific code from aiida.manage.configuration
Browse files Browse the repository at this point in the history
To compile the documentation, the code has to be imported which includes
the Django database models. Unfortunately, Django will raise here if it
has not been configured. Normally this is done through the backend which
will load the backend environment for the currently loaded AiiDA profile.
Since on readthedocs.org AiiDA is not installed and so there won't be a
configuration with a profile to load, the profile management code was
monkey patched to provide a dummy profile just for this purpose.

Instead of cluttering the main codebase for this one exception, we move
the responsibility to the documentation configuration itself. The
requirements boil down to the Django settings being called and an AiiDA
configuration and profile to be loaded. Since loading a profile with a
django backend and loading said backend, the former will be accomplished
automatically. This will now also allow building the documentation
locally even if the default profile is not using a Django backend
because the dummy documentation profile will simply be used. The function
`aiida.manage.configuration.load_documentation_profile` performs all the
required actions.
  • Loading branch information
sphuber committed Dec 14, 2019
1 parent c630b78 commit f51c231
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 57 deletions.
72 changes: 37 additions & 35 deletions aiida/manage/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
###########################################################################
# pylint: disable=undefined-variable,wildcard-import,global-statement,redefined-outer-name,cyclic-import
"""Modules related to the configuration of an AiiDA instance."""

import warnings

from aiida.common.warnings import AiidaDeprecationWarning
from .config import *
from .options import *
Expand All @@ -20,9 +20,6 @@
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')
Expand Down Expand Up @@ -64,14 +61,8 @@ def load_profile(profile=None):
# 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()

if IN_RT_DOC_MODE:
# on readthedocs, already load backend (no schema check since no DB)
from aiida.manage.manager import get_manager
get_manager()._load_backend(schema_check=False) # pylint: disable=protected-access

return PROFILE


Expand Down Expand Up @@ -159,31 +150,7 @@ def get_config(create=False):
global CONFIG

if not CONFIG:
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
import tempfile
CONFIG = Config(
tempfile.mkstemp()[1], {
'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',
'AIIDADB_REPOSITORY_URI': 'file:///tmp/repository',
'AIIDADB_USER': 'aiida'
}
}
}
)
else:
CONFIG = load_config(create=create)
CONFIG = load_config(create=create)

if CONFIG.get_option('warnings.showdeprecations'):
# If the user does not want to get AiiDA deprecation warnings, we disable them - this can be achieved with::
Expand Down Expand Up @@ -230,3 +197,38 @@ def get_config_option(option_name):
value = value_profile if value_profile else config.get_option(option_name)

return value


def load_documentation_profile():
"""Load a dummy profile just for the purposes of being able to build the documentation.
The building of the documentation will require importing the `aiida` package and some code will try to access the
loaded configuration and profile, which if not done will except. On top of that, Django will raise an exception if
the database models are loaded before its settings are loaded. This also is taken care of by loading a Django
profile and loading the corresponding backend. Calling this function will perform all these requirements allowing
the documentation to be built without having to install and configure AiiDA nor having an actual database present.
"""
import tempfile
from aiida.manage.manager import get_manager
from .config import Config
from .profile import Profile

global PROFILE
global CONFIG

with tempfile.NamedTemporaryFile() as handle:
profile_name = 'readthedocs'
profile = {
'AIIDADB_ENGINE': 'postgresql_psycopg2',
'AIIDADB_BACKEND': 'django',
'AIIDADB_PORT': 5432,
'AIIDADB_HOST': 'localhost',
'AIIDADB_NAME': 'aiidadb',
'AIIDADB_PASS': 'aiidadb',
'AIIDADB_USER': 'aiida',
'AIIDADB_REPOSITORY_URI': 'file:///dev/null',
}
config = {'default_profile': profile_name, 'profiles': {profile_name: profile}}
PROFILE = Profile(profile_name, profile, from_config=True)
CONFIG = Config(handle.name, config)
get_manager()._load_backend(schema_check=False) # pylint: disable=protected-access
34 changes: 12 additions & 22 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,24 @@
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import os
import sys

# Following 3 lines avoid the need of importing load_dbenv() for compiling the documentation -> works also without verdi install
# Add `aiida` to the path so it can be imported without installing it
sys.path.append(os.path.join(os.path.split(__file__)[0], os.pardir, os.pardir))

import aiida
from aiida.manage import configuration
from aiida.manage.configuration import load_documentation_profile

# Load the dummy profile even if we are running locally, this way the documentation will succeed even if the current
# default profile of the AiiDA installation does not use a Django backend.
load_documentation_profile()

# If we are not on READTHEDOCS load the Sphinx theme manually
if not os.environ.get('READTHEDOCS', None):
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
Expand Down Expand Up @@ -233,25 +242,6 @@
# If false, no module index is generated.
#latex_domain_indices = True

# on_rtd is whether we are on readthedocs.org
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'


if on_rtd:
# Set the global variable to trigger shortcut behavior in `aiida.manage.configuration.load_config`
configuration.IN_RT_DOC_MODE = True
else:
try:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
except ImportError: # No sphinx_rtd_theme installed
pass

# load AiiDA profile
configuration.load_profile()


def run_apidoc(_):
"""Runs sphinx-apidoc when building the documentation.
Expand Down

0 comments on commit f51c231

Please sign in to comment.