def _get_repository_path(self): from aiida.backends import settings from aiida.common.setup import (get_config, get_profile_config, parse_repository_uri) from aiida.common.exceptions import ConfigurationError try: confs = get_config() except ConfigurationError: raise ConfigurationError( "Please run the AiiDA Installation, no config found") if settings.AIIDADB_PROFILE is None: raise ConfigurationError( "settings.AIIDADB_PROFILE not defined, did you load django" "through the AiiDA load_dbenv()?") profile_conf = get_profile_config(settings.AIIDADB_PROFILE, conf_dict=confs) REPOSITORY_URI = profile_conf.get('AIIDADB_REPOSITORY_URI', '') REPOSITORY_PROTOCOL, REPOSITORY_PATH = parse_repository_uri( REPOSITORY_URI) return REPOSITORY_PATH
def __init__(self, **kwargs): """ Initialize the job resources from the passed arguments (the valid keys can be obtained with the function self.get_valid_keys()). :raise ValueError: on invalid parameters. :raise TypeError: on invalid parameters. :raise aiida.common.ConfigurationError: if default_mpiprocs_per_machine was set for this computer, since LsfJobResource cannot accept this parameter. """ from aiida.common.exceptions import ConfigurationError super().__init__() self.parallel_env = kwargs.pop('parallel_env', '') if not isinstance(self.parallel_env, str): raise TypeError("When specified, 'parallel_env' must be a string") try: self.tot_num_mpiprocs = int(kwargs.pop('tot_num_mpiprocs')) except (KeyError, ValueError) as exc: raise TypeError('tot_num_mpiprocs must be specified and must be an integer') from exc default_mpiprocs_per_machine = kwargs.pop('default_mpiprocs_per_machine', None) if default_mpiprocs_per_machine is not None: raise ConfigurationError('default_mpiprocs_per_machine cannot be set for LSF scheduler') num_machines = kwargs.pop('num_machines', None) if num_machines is not None: raise ConfigurationError('num_machines cannot be set for LSF scheduler') if self.tot_num_mpiprocs <= 0: raise ValueError('tot_num_mpiprocs must be >= 1')
def get_dbauthinfo(computer, aiidauser): """ Given a computer and a user, returns a DbAuthInfo object :param computer: a computer (can be a string, Computer or DbComputer instance) :param aiidauser: a user, can be a DbUser or a User instance :return: a DbAuthInfo instance :raise NotExistent: if the user is not configured to use computer :raise sqlalchemy.orm.exc.MultipleResultsFound: if the user is configured more than once to use the computer! Should never happen """ from aiida.common.exceptions import InternalError if settings.BACKEND == BACKEND_DJANGO: from aiida.backends.djsite.db.models import DbComputer, DbAuthInfo, DbUser from django.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned) try: authinfo = DbAuthInfo.objects.get( # converts from name, Computer or DbComputer instance to # a DbComputer instance dbcomputer=DbComputer.get_dbcomputer(computer), aiidauser=aiidauser.id) except ObjectDoesNotExist: raise NotExistent( "The aiida user {} is not configured to use computer {}". format(aiidauser.email, computer.name)) except MultipleObjectsReturned: raise ConfigurationError( "The aiida user {} is configured more than once to use " "computer {}! Only one configuration is allowed".format( aiidauser.email, computer.name)) elif settings.BACKEND == BACKEND_SQLA: from aiida.backends.sqlalchemy.models.authinfo import DbAuthInfo from aiida.backends.sqlalchemy import get_scoped_session session = get_scoped_session() from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound try: authinfo = session.query(DbAuthInfo).filter_by( dbcomputer_id=computer.id, aiidauser_id=aiidauser.id, ).one() except NoResultFound: raise NotExistent( "The aiida user {} is not configured to use computer {}". format(aiidauser.email, computer.name)) except MultipleResultsFound: raise ConfigurationError( "The aiida user {} is configured more than once to use " "computer {}! Only one configuration is allowed".format( aiidauser.email, computer.name)) else: raise InternalError("unknown backend {}".format(settings.BACKEND)) return authinfo
def get_db_test_list(): """ This function returns the db_test_list for the current backend, merged with the 'common' tests. :note: This function should be called only after setting the backend, and then it returns only the tests for this backend, and the common ones. """ from aiida.backends import settings from aiida.common.exceptions import ConfigurationError from collections import defaultdict current_backend = settings.BACKEND try: be_tests = db_test_list[current_backend] except KeyError: raise ConfigurationError("No backend configured yet") # Could be undefined, so put to empty dict by default try: common_tests = db_test_list["common"] except KeyError: raise ConfigurationError("A 'common' key must always be defined!") retdict = defaultdict(list) for k, tests in common_tests.iteritems(): for t in tests: retdict[k].append(t) for k, tests in be_tests.iteritems(): for t in tests: retdict[k].append(t) # This is a temporary solution to be able to run tests in plugins. Once the plugin fixtures # have been made working and are released, we can replace this logic with them for ep in [ep for ep in epm.iter_entry_points(group='aiida.tests')]: retdict[ep.name].append(ep.module_name) # Explode the dictionary so that if I have a.b.c, # I can run it also just with 'a' or with 'a.b' final_retdict = defaultdict(list) for k, v in retdict.iteritems(): final_retdict[k] = v for k, v in retdict.iteritems(): if '.' in k: parts = k.split('.') for last_idx in range(1, len(parts)): parentkey = ".".join(parts[:last_idx]) final_retdict[parentkey].extend(v) return dict(final_retdict)
def table2resource(table_name): # TODO Consider ways to make this function backend independent (one # idea would be to go from table name to aiida class name which is # univoque) if BACKEND == BACKEND_DJANGO: (spam, resource_name) = issingular(table_name[2:].lower()) elif BACKEND == BACKEND_SQLA: (spam, resource_name) = issingular(table_name[5:]) elif BACKEND is None: raise ConfigurationError("settings.BACKEND has not been set.\n" "Hint: Have you called " "aiida.load_dbenv?") else: raise ConfigurationError("Unknown settings.BACKEND: {}".format( BACKEND)) return resource_name
def run_migrations_online(): """Run migrations in 'online' mode. The connection should have been passed to the config, which we use to configue the migration context. """ # pylint: disable=unused-import from aiida.backends.sqlalchemy.models.authinfo import DbAuthInfo from aiida.backends.sqlalchemy.models.comment import DbComment from aiida.backends.sqlalchemy.models.computer import DbComputer from aiida.backends.sqlalchemy.models.group import DbGroup from aiida.backends.sqlalchemy.models.log import DbLog from aiida.backends.sqlalchemy.models.node import DbLink, DbNode from aiida.backends.sqlalchemy.models.settings import DbSetting from aiida.backends.sqlalchemy.models.user import DbUser from aiida.common.exceptions import DbContentError from aiida.backends.sqlalchemy.models.base import Base config = context.config # pylint: disable=no-member connectable = config.attributes.get('connection', None) if connectable is None: from aiida.common.exceptions import ConfigurationError raise ConfigurationError( 'An initialized connection is expected for the AiiDA online migrations.' ) with connectable.connect() as connection: context.configure( # pylint: disable=no-member connection=connection, target_metadata=Base.metadata, transaction_per_migration=True, ) context.run_migrations() # pylint: disable=no-member
def _get_config(config_file): try: with open(config_file, 'r') as f: config = yaml.load(f)[get_current_profile()] # no config file, or no config for this profile except (OSError, IOError, KeyError): return DEFAULT_CONFIG # validate configuration for key in config: if key not in DEFAULT_CONFIG: raise ValueError( "Configuration error: Invalid key '{}' in cache_config.yml".format(key) ) # add defaults where config is missing for key, default_config in DEFAULT_CONFIG.items(): config[key] = config.get(key, default_config) # load classes try: for key in [config_keys.enabled, config_keys.disabled]: config[key] = [load_class(c) for c in config[key]] except (ImportError, ClassNotFoundException) as err: raise_from( ConfigurationError("Unknown class given in 'cache_config.yml': '{}'".format(err)), err ) return config
def check_schema_version(profile_name): """ Check if the version stored in the database is the same of the version of the code. :raise aiida.common.ConfigurationError: if the two schema versions do not match """ from aiida.backends import sqlalchemy as sa from aiida.common.exceptions import ConfigurationError alembic_cfg = get_alembic_conf() validate_schema_generation() # Getting the version of the code and the database, reusing the existing engine (initialized by AiiDA) with sa.ENGINE.begin() as connection: alembic_cfg.attributes['connection'] = connection code_schema_version = get_migration_head(alembic_cfg) db_schema_version = get_db_schema_version(alembic_cfg) if code_schema_version != db_schema_version: raise ConfigurationError( 'Database schema version {} is outdated compared to the code schema version {}\n' 'Before you upgrade, make sure all calculations and workflows have finished running.\n' 'If this is not the case, revert the code to the previous version and finish them first.\n' 'To migrate the database to the current version, run the following commands:' '\n verdi -p {} daemon stop\n verdi -p {} database migrate'. format(db_schema_version, code_schema_version, profile_name, profile_name))
def run_migrations_online(): """Run migrations in 'online' mode. In this scenario we need to create an Engine and associate a connection with the context. """ # This is the Alembic Config object, which provides # access to the values within the .ini file in use. # It is initialized by alembic and we enrich it here # to point to the right database configuration. config = context.config connectable = config.attributes.get('connection', None) if connectable is None: from aiida.common.exceptions import ConfigurationError raise ConfigurationError("An initialized connection is expected " "for the AiiDA online migrations.") with connectable.connect() as connection: context.configure(connection=connection, target_metadata=target_metadata) with context.begin_transaction(): context.run_migrations()
def get_repository_folder(subfolder=None): """ Return the top folder of the local repository. """ try: return _repository_folder_cache[subfolder] except KeyError: try: from aiida.settings import REPOSITORY_PATH if not os.path.isdir(REPOSITORY_PATH): raise ImportError except ImportError: raise ConfigurationError( "The REPOSITORY_PATH variable is not set correctly.") if subfolder is None: retval = os.path.abspath(REPOSITORY_PATH) elif subfolder == "sandbox": retval = os.path.abspath(os.path.join(REPOSITORY_PATH, 'sandbox')) elif subfolder == "repository": retval = os.path.abspath( os.path.join(REPOSITORY_PATH, 'repository')) else: raise ValueError("Invalid 'subfolder' passed to " "get_repository_folder: {}".format(subfolder)) _repository_folder_cache[subfolder] = retval return retval
def get_transport_class(self): try: # I return the class, not an instance return TransportFactory(self.get_transport_type()) except MissingPluginError as e: raise ConfigurationError('No transport found for {} [type {}], message: {}'.format( self.name, self.get_transport_type(), e.message))
def get_backend_class(cls): """Get backend class.""" from aiida.backends.testimplbase import AiidaTestImplementation from aiida.backends import BACKEND_SQLA, BACKEND_DJANGO from aiida.manage.configuration import PROFILE # Freeze the __impl_class after the first run if not hasattr(cls, '__impl_class'): if PROFILE.database_backend == BACKEND_SQLA: from aiida.backends.sqlalchemy.testbase import SqlAlchemyTests cls.__impl_class = SqlAlchemyTests elif PROFILE.database_backend == BACKEND_DJANGO: from aiida.backends.djsite.db.testbase import DjangoTests cls.__impl_class = DjangoTests else: raise ConfigurationError('Unknown backend type') # Check that it is of the right class if not issubclass(cls.__impl_class, AiidaTestImplementation): raise InternalError( 'The AiiDA test implementation is not of type ' '{}, that is not a subclass of AiidaTestImplementation'. format(cls.__impl_class.__name__)) return cls.__impl_class
def __init__(self, **kwargs): """ Initialize the job resources from the passed arguments (the valid keys can be obtained with the function self.get_valid_keys()). :raise ValueError: on invalid parameters. :raise TypeError: on invalid parameters. :raise ConfigurationError: if default_mpiprocs_per_machine was set for this computer, since ParEnvJobResource cannot accept this parameter. """ from aiida.common.exceptions import ConfigurationError try: self.parallel_env = str(kwargs.pop('parallel_env')) except (KeyError, TypeError, ValueError): raise TypeError("'parallel_env' must be specified and must be a string") try: self.tot_num_mpiprocs = int(kwargs.pop('tot_num_mpiprocs')) except (KeyError, ValueError): raise TypeError("tot_num_mpiprocs must be specified and must be an integer") default_mpiprocs_per_machine = kwargs.pop('default_mpiprocs_per_machine', None) if default_mpiprocs_per_machine is not None: raise ConfigurationError("default_mpiprocs_per_machine cannot be set " "for schedulers that use ParEnvJobResource") if self.tot_num_mpiprocs <= 0: raise ValueError("tot_num_mpiprocs must be >= 1")
def get_schema(self): # Construct the full class string class_string = 'aiida.orm.' + self._aiida_type # Load correspondent orm class orm_class = get_object_from_string(class_string) # Construct the json object to be returned basic_schema = orm_class.get_schema() schema = {} ordering = [] # get addional info and column order from translator class # and combine it with basic schema if len(self._schema_projections["column_order"]) > 0: for field in self._schema_projections["column_order"]: # basic schema if field in basic_schema.keys(): schema[field] = basic_schema[field] else: ## Note: if column name starts with user_* get the schema information from # user class. It is added mainly to handle user_email case. # TODO need to improve field_parts = field.split("_") if field_parts[0] == "user" and field != "user_id" and len( field_parts) > 1: from aiida.orm.user import User user_schema = User.get_schema() if field_parts[1] in user_schema.keys(): schema[field] = user_schema[field_parts[1]] else: raise KeyError( "{} is not present in user schema".format( field)) else: raise KeyError( "{} is not present in ORM basic schema".format( field)) # additional info defined in translator class if field in self._schema_projections["additional_info"]: schema[field].update( self._schema_projections["additional_info"][field]) else: raise KeyError( "{} is not present in default projection additional info" .format(field)) # order ordering = self._schema_projections["column_order"] else: raise ConfigurationError( "Define column order to get schema for {}".format( self._aiida_type)) return dict(fields=schema, ordering=ordering)
def load_dbenv(process=None, profile=None, *args, **kwargs): if is_dbenv_loaded(): raise InvalidOperation("You cannot call load_dbenv multiple times!") settings.LOAD_DBENV_CALLED = True # This is going to set global variables in settings, including # settings.BACKEND load_profile(process=process, profile=profile) if settings.BACKEND == BACKEND_SQLA: # Maybe schema version should be also checked for SQLAlchemy version. from aiida.backends.sqlalchemy.utils \ import load_dbenv as load_dbenv_sqlalchemy return load_dbenv_sqlalchemy(process=process, profile=profile, *args, **kwargs) elif settings.BACKEND == BACKEND_DJANGO: from aiida.backends.djsite.utils import load_dbenv as load_dbenv_django return load_dbenv_django(process=process, profile=profile, *args, **kwargs) else: raise ConfigurationError("Invalid settings.BACKEND: {}".format( settings.BACKEND))
def get_scheduler(self): try: ThisPlugin = SchedulerFactory(self.get_scheduler_type()) # I call the init without any parameter return ThisPlugin() except MissingPluginError as e: raise ConfigurationError('No scheduler found for {} [type {}], message: {}'.format( self.name, self.get_scheduler_type(), e.message))
def get_authinfo(computer, aiidauser): if settings.BACKEND == BACKEND_DJANGO: from aiida.backends.djsite.db.models import DbComputer, DbAuthInfo from django.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned) try: authinfo = DbAuthInfo.objects.get( # converts from name, Computer or DbComputer instance to # a DbComputer instance dbcomputer=DbComputer.get_dbcomputer(computer), aiidauser=aiidauser) except ObjectDoesNotExist: raise AuthenticationError( "The aiida user {} is not configured to use computer {}". format(aiidauser.email, computer.name)) except MultipleObjectsReturned: raise ConfigurationError( "The aiida user {} is configured more than once to use " "computer {}! Only one configuration is allowed".format( aiidauser.email, computer.name)) elif settings.BACKEND == BACKEND_SQLA: from aiida.backends.sqlalchemy.models.authinfo import DbAuthInfo from aiida.backends.sqlalchemy import get_scoped_session session = get_scoped_session() from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound try: authinfo = session.query(DbAuthInfo).filter_by( dbcomputer_id=computer.id, aiidauser_id=aiidauser.id, ).one() except NoResultFound: raise AuthenticationError( "The aiida user {} is not configured to use computer {}". format(aiidauser.email, computer.name)) except MultipleResultsFound: raise ConfigurationError( "The aiida user {} is configured more than once to use " "computer {}! Only one configuration is allowed".format( aiidauser.email, computer.name)) else: raise Exception("unknown backend {}".format(settings.BACKEND)) return authinfo
def get_db_test_list(): """ This function returns the db_test_list for the current backend, merged with the 'common' tests. :note: This function should be called only after setting the backend, and then it returns only the tests for this backend, and the common ones. """ from aiida.backends import settings from aiida.common.exceptions import ConfigurationError from collections import defaultdict current_backend = settings.BACKEND try: be_tests = db_test_list[current_backend] except KeyError: raise ConfigurationError("No backend configured yet") # Could be undefined, so put to empty dict by default try: common_tests = db_test_list["common"] except KeyError: raise ConfigurationError("A 'common' key must always be defined!") retdict = defaultdict(list) for k, tests in common_tests.iteritems(): for t in tests: retdict[k].append(t) for k, tests in be_tests.iteritems(): for t in tests: retdict[k].append(t) # Explode the dictionary so that if I have a.b.c, # I can run it also just with 'a' or with 'a.b' final_retdict = defaultdict(list) for k, v in retdict.iteritems(): final_retdict[k] = v for k, v in retdict.iteritems(): if '.' in k: parts = k.split('.') for last_idx in range(1, len(parts)): parentkey = ".".join(parts[:last_idx]) final_retdict[parentkey].extend(v) return dict(final_retdict)
def QueryFactory(): if settings.BACKEND == BACKEND_SQLA: from aiida.backends.sqlalchemy.queries import QueryManagerSQLA as QueryManager elif settings.BACKEND == BACKEND_DJANGO: from aiida.backends.djsite.queries import QueryManagerDjango as QueryManager else: raise ConfigurationError("Invalid settings.BACKEND: {}".format( settings.BACKEND)) return QueryManager
def cmd_string(self): """ Return the command string to start the AiiDA daemon """ from aiida.common.exceptions import ConfigurationError if VERDI_BIN is None: raise ConfigurationError("Unable to find 'verdi' in the path. Make sure that you are working " "in a virtual environment, or that at least the 'verdi' executable is on the PATH") return '{} -p {} devel run_daemon'.format(VERDI_BIN, self.profile_name)
def get_db_test_list(): """ This function returns the DB_TEST_LIST for the current backend, merged with the 'common' tests. :note: This function should be called only after setting the backend, and then it returns only the tests for this backend, and the common ones. """ from collections import defaultdict from aiida.common.exceptions import ConfigurationError from aiida.manage import configuration try: be_tests = DB_TEST_LIST[configuration.PROFILE.database_backend] except KeyError: raise ConfigurationError('No backend configured yet') # Could be undefined, so put to empty dict by default try: common_tests = DB_TEST_LIST['common'] except KeyError: raise ConfigurationError("A 'common' key must always be defined!") retdict = defaultdict(list) for k, tests in common_tests.items(): for test in tests: retdict[k].append(test) for k, tests in be_tests.items(): for test in tests: retdict[k].append(test) # Explode the dictionary so that if I have a.b.c, # I can run it also just with 'a' or with 'a.b' final_retdict = defaultdict(list) for key, val in retdict.items(): final_retdict[key] = val for key, val in retdict.items(): if '.' in key: parts = key.split('.') for last_idx in range(1, len(parts)): parentkey = '.'.join(parts[:last_idx]) final_retdict[parentkey].extend(val) return dict(final_retdict)
def get_profiles_list(): """ Return the list of names of installed configurations """ from aiida.common.exceptions import ConfigurationError all_config = get_config() try: return all_config['profiles'].keys() except KeyError: return ConfigurationError("Please run the setup")
def parse_repository_uri(repository_uri): """ This function validates the REPOSITORY_URI, that should be in the format protocol://address :note: At the moment, only the file protocol is supported. :return: a tuple (protocol, address). """ import uritools parts = uritools.urisplit(repository_uri) if parts.scheme != u'file': raise ConfigurationError("The current AiiDA version supports only a " "local repository") if parts.scheme == u'file': if not os.path.isabs(parts.path): raise ConfigurationError("The current repository is specified with a " "file protocol but with a relative path") # Normalize path to its absolute path return parts.scheme, os.path.expanduser(parts.path)
def table2resource(table_name): """ Convert the related_tablevalues to the RESTAPI resources (orm class/db table ==> RESTapi resource) :param table_name (str): name of the table (in SQLA is __tablename__) :return: resource_name (str): name of the API resource """ # TODO Consider ways to make this function backend independent (one # idea would be to go from table name to aiida class name which is # unique) if BACKEND == BACKEND_DJANGO: (spam, resource_name) = issingular(table_name[2:].lower()) elif BACKEND == BACKEND_SQLA: (spam, resource_name) = issingular(table_name[5:]) elif BACKEND is None: raise ConfigurationError("settings.BACKEND has not been set.\n" "Hint: Have you called " "aiida.load_dbenv?") else: raise ConfigurationError( "Unknown settings.BACKEND: {}".format(BACKEND)) return resource_name
def load_dbenv(profile=None, *args, **kwargs): if configuration.PROFILE.database_backend == BACKEND_SQLA: # Maybe schema version should be also checked for SQLAlchemy version. from aiida.backends.sqlalchemy.utils \ import load_dbenv as load_dbenv_sqlalchemy to_return = load_dbenv_sqlalchemy(profile=profile, *args, **kwargs) elif configuration.PROFILE.database_backend == BACKEND_DJANGO: from aiida.backends.djsite.utils import load_dbenv as load_dbenv_django to_return = load_dbenv_django(profile=profile, *args, **kwargs) else: raise ConfigurationError( 'Invalid configuration.PROFILE.database_backend: {}'.format( configuration.PROFILE.database_backend)) return to_return
def validate_schema_generation(): """Verify that the database schema generation is compatible with the current code schema generation.""" from aiida.common.exceptions import ConfigurationError, NotExistent try: schema_generation_database = get_db_schema_generation().value except NotExistent: schema_generation_database = '1' if schema_generation_database is None: schema_generation_database = '1' if schema_generation_database != SCHEMA_GENERATION_VALUE: raise ConfigurationError( 'The schema generation of your database {} is newer than that of the code `{}` and is incompatible.' .format(schema_generation_database, SCHEMA_GENERATION_VALUE))
def parse_repository_uri(repository_uri): """ This function validates the REPOSITORY_URI, that should be in the format protocol://address :note: At the moment, only the file protocol is supported. :return: a tuple (protocol, address). """ protocol, _, address = repository_uri.partition('://') if protocol != 'file': raise ConfigurationError("The current AiiDA version supports only a " "local repository") if protocol == 'file': if not os.path.isabs(address): raise ConfigurationError("The current repository is specified with a " "file protocol but with a relative path") address = os.path.expanduser(address) # Normalize address to its absolute path return (protocol, address)
def get_automatic_user(): # global _aiida_autouser_cache # if _aiida_autouser_cache is not None: # return _aiida_autouser_cache from aiida.backends.sqlalchemy.models.user import DbUser from aiida.common.utils import get_configured_user_email email = get_configured_user_email() _aiida_autouser_cache = DbUser.query.filter(DbUser.email == email).first() if not _aiida_autouser_cache: raise ConfigurationError("No aiida user with email {}".format(email)) return _aiida_autouser_cache
def get_transport(self): """ Return a configured transport to connect to the computer. """ computer = self.computer try: ThisTransport = TransportFactory(computer.get_transport_type()) except MissingPluginError as e: raise ConfigurationError( 'No transport found for {} [type {}], message: {}'.format( computer.hostname, computer.get_transport_type(), e.message)) params = dict(computer.get_transport_params().items() + self.get_auth_params().items()) return ThisTransport(machine=computer.hostname, **params)
def check_schema_version(): """ Check if the version stored in the database is the same of the version of the code. :note: if the DbSetting table does not exist, this function does not fail. The reason is to avoid to have problems before running the first migrate call. :note: if no version is found, the version is set to the version of the code. This is useful to have the code automatically set the DB version at the first code execution. :raise ConfigurationError: if the two schema versions do not match. Otherwise, just return. """ import os import aiida.backends.djsite.db.models from aiida.backends.utils import get_current_profile from django.db import connection from aiida.common.exceptions import ConfigurationError # Do not do anything if the table does not exist yet if 'db_dbsetting' not in connection.introspection.table_names(): return code_schema_version = aiida.backends.djsite.db.models.SCHEMA_VERSION db_schema_version = get_db_schema_version() if db_schema_version is None: # No code schema defined yet, I set it to the code version set_db_schema_version(code_schema_version) db_schema_version = get_db_schema_version() filepath_utils = os.path.abspath(__file__) filepath_manage = os.path.join(os.path.dirname(filepath_utils), 'manage.py') if code_schema_version != db_schema_version: raise ConfigurationError( "The code schema version is {}, but the version stored in the " "database (DbSetting table) is {}, stopping.\n" "To migrate the database to the current version, run the following commands:" "\n verdi daemon stop\n python {} --aiida-profile={} migrate". format(code_schema_version, db_schema_version, filepath_manage, get_current_profile()))