From a9980a2438f3b7ee6ac71566eea5955473f858e4 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 22 Nov 2024 01:45:52 +0100 Subject: [PATCH] Resolver to resolve, AiiDAConfigDir is singleton --- src/aiida/__init__.py | 4 +- src/aiida/cmdline/commands/cmd_presto.py | 4 +- src/aiida/cmdline/commands/cmd_profile.py | 4 +- src/aiida/cmdline/commands/cmd_status.py | 4 +- .../cmdline/params/options/commands/setup.py | 4 +- src/aiida/manage/configuration/__init__.py | 4 +- src/aiida/manage/configuration/settings.py | 52 +++++++++++-------- src/aiida/storage/sqlite_dos/backend.py | 4 +- .../tools/pytest_fixtures/configuration.py | 6 +-- tests/conftest.py | 6 +-- tests/manage/configuration/test_config.py | 24 ++++----- tests/manage/test_caching_config.py | 6 +-- 12 files changed, 64 insertions(+), 58 deletions(-) diff --git a/src/aiida/__init__.py b/src/aiida/__init__.py index 4927ded769..42c5e8299b 100644 --- a/src/aiida/__init__.py +++ b/src/aiida/__init__.py @@ -21,7 +21,7 @@ from aiida.common.log import configure_logging # noqa: F401 from aiida.manage.configuration import get_config_option, get_profile, load_profile, profile_context # noqa: F401 -from aiida.manage.configuration.settings import AiiDAConfigPathResolver +from aiida.manage.configuration.settings import AiiDAConfigDir __copyright__ = ( 'Copyright (c), This file is part of the AiiDA platform. ' @@ -37,7 +37,7 @@ __paper_short__ = 'S. P. Huber et al., Scientific Data 7, 300 (2020).' # Initialize the configuration directory settings -AiiDAConfigPathResolver.set_configuration_directory() +AiiDAConfigDir.set_configuration_directory() def get_strict_version(): diff --git a/src/aiida/cmdline/commands/cmd_presto.py b/src/aiida/cmdline/commands/cmd_presto.py index 948a270609..7c396e602d 100644 --- a/src/aiida/cmdline/commands/cmd_presto.py +++ b/src/aiida/cmdline/commands/cmd_presto.py @@ -67,7 +67,7 @@ def detect_postgres_config( """ import secrets - from aiida.manage.configuration.settings import AiiDAConfigPathResolver + from aiida.manage.configuration.settings import AiiDAConfigDir from aiida.manage.external.postgres import Postgres dbinfo = { @@ -92,7 +92,7 @@ def detect_postgres_config( except Exception as exception: raise ConnectionError(f'Unable to automatically create the PostgreSQL user and database: {exception}') - aiida_config_folder = AiiDAConfigPathResolver.get_configuration_directory() + aiida_config_folder = AiiDAConfigDir.get_configuration_directory() return { 'database_hostname': postgres_hostname, diff --git a/src/aiida/cmdline/commands/cmd_profile.py b/src/aiida/cmdline/commands/cmd_profile.py index 0073485965..35f00b7bdd 100644 --- a/src/aiida/cmdline/commands/cmd_profile.py +++ b/src/aiida/cmdline/commands/cmd_profile.py @@ -169,9 +169,9 @@ def profile_list(): # This can happen for a fresh install and the `verdi setup` has not yet been run. In this case it is still nice # to be able to see the configuration directory, for instance for those who have set `AIIDA_PATH`. This way # they can at least verify that it is correctly set. - from aiida.manage.configuration.settings import AiiDAConfigPathResolver + from aiida.manage.configuration.settings import AiiDAConfigDir - echo.echo_report(f'configuration folder: {AiiDAConfigPathResolver.get_configuration_directory()}') + echo.echo_report(f'configuration folder: {AiiDAConfigDir.get_configuration_directory()}') echo.echo_critical(str(exception)) else: echo.echo_report(f'configuration folder: {config.dirpath}') diff --git a/src/aiida/cmdline/commands/cmd_status.py b/src/aiida/cmdline/commands/cmd_status.py index 6683c194fa..72d9698ddf 100644 --- a/src/aiida/cmdline/commands/cmd_status.py +++ b/src/aiida/cmdline/commands/cmd_status.py @@ -61,11 +61,11 @@ def verdi_status(print_traceback, no_rmq): from aiida.common.docs import URL_NO_BROKER from aiida.common.exceptions import ConfigurationError from aiida.engine.daemon.client import DaemonException, DaemonNotRunningException - from aiida.manage.configuration.settings import AiiDAConfigPathResolver + from aiida.manage.configuration.settings import AiiDAConfigDir from aiida.manage.manager import get_manager exit_code = ExitCode.SUCCESS - configure_directory = AiiDAConfigPathResolver.get_configuration_directory() + configure_directory = AiiDAConfigDir.get_configuration_directory() print_status(ServiceStatus.UP, 'version', f'AiiDA v{__version__}') print_status(ServiceStatus.UP, 'config', configure_directory) diff --git a/src/aiida/cmdline/params/options/commands/setup.py b/src/aiida/cmdline/params/options/commands/setup.py index d60f70a274..0845ac34bf 100644 --- a/src/aiida/cmdline/params/options/commands/setup.py +++ b/src/aiida/cmdline/params/options/commands/setup.py @@ -66,10 +66,10 @@ def get_repository_uri_default(ctx): """ import os - from aiida.manage.configuration.settings import AiiDAConfigPathResolver + from aiida.manage.configuration.settings import AiiDAConfigDir validate_profile_parameter(ctx) - configure_directory = AiiDAConfigPathResolver.get_configuration_directory() + configure_directory = AiiDAConfigDir.get_configuration_directory() return os.path.join(configure_directory, 'repository', ctx.params['profile'].name) diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index eae8c65e08..c56abae2c1 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -11,7 +11,7 @@ from __future__ import annotations -from aiida.manage.configuration.settings import AiiDAConfigPathResolver +from aiida.manage.configuration.settings import AiiDAConfigDir # AUTO-GENERATED # fmt: off @@ -70,7 +70,7 @@ def get_config_path(): """Returns path to aiida configuration file.""" from .settings import DEFAULT_CONFIG_FILE_NAME - return os.path.join(AiiDAConfigPathResolver.get_configuration_directory(), DEFAULT_CONFIG_FILE_NAME) + return os.path.join(AiiDAConfigDir.get_configuration_directory(), DEFAULT_CONFIG_FILE_NAME) def load_config(create=False) -> 'Config': diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 2cb22e09d2..5877df0e7a 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -26,19 +26,42 @@ DEFAULT_DAEMON_LOG_DIR_NAME = 'log' DEFAULT_ACCESS_CONTROL_DIR_NAME = 'access' -__all__ = ('AiiDAConfigPathResolver',) +__all__ = ('AiiDAConfigPathResolver', 'AiiDAConfigDir') @final -class AiiDAConfigPathResolver: - """Path resolver for setting and getting the path to configuration directory, daemon dir, - daemon log dir and access control dir. The locations are all trivially derived from the config location, - The class provide dedicated setter/getter for configuration_directory.""" +class AiiDAConfigDir: + """Singleton for setting and getting the path to configuration directory.""" _glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME + @classmethod + def get_configuration_directory(cls): + """Return the path of the configuration directory.""" + return cls._glb_aiida_config_folder + + @classmethod + def set_configuration_directory(cls, aiida_config_folder: pathlib.Path | None = None) -> None: + """Set the configuration directory, related global variables and create instance directories. + + The location of the configuration directory is defined by ``aiida_config_folder`` or if not defined, + the path that is returned by ``get_configuration_directory_from_envvar``. If the directory does not exist yet, + it is created, together with all its subdirectories. + """ + cls._glb_aiida_config_folder = aiida_config_folder or _get_configuration_directory_from_envvar() + + _create_instance_directories(cls._glb_aiida_config_folder) + + +@final +class AiiDAConfigPathResolver: + """For resolving configuration directory, daemon dir, + daemon log dir and access control dir. + The locations are all trivially derived from the config location, + """ + def __init__(self, config_folder: pathlib.Path | None) -> None: - self._aiida_path = config_folder or self._glb_aiida_config_folder + self._aiida_path = config_folder or AiiDAConfigDir.get_configuration_directory() @property def aiida_path(self) -> pathlib.Path: @@ -56,23 +79,6 @@ def daemon_log_dir(self) -> pathlib.Path: def access_control_dir(self) -> pathlib.Path: return self._aiida_path / DEFAULT_ACCESS_CONTROL_DIR_NAME - @classmethod - def get_configuration_directory(cls): - """Return the path of the configuration directory.""" - return cls._glb_aiida_config_folder - - @classmethod - def set_configuration_directory(cls, aiida_config_folder: pathlib.Path | None = None) -> None: - """Set the configuration directory, related global variables and create instance directories. - - The location of the configuration directory is defined by ``aiida_config_folder`` or if not defined, - the path that is returned by ``get_configuration_directory_from_envvar``. If the directory does not exist yet, - it is created, together with all its subdirectories. - """ - cls._glb_aiida_config_folder = aiida_config_folder or _get_configuration_directory_from_envvar() - - _create_instance_directories(cls._glb_aiida_config_folder) - def _create_instance_directories(aiida_config_folder: pathlib.Path | None) -> None: """Create the base directories required for a new AiiDA instance. diff --git a/src/aiida/storage/sqlite_dos/backend.py b/src/aiida/storage/sqlite_dos/backend.py index ff24b17a6a..cf1625db01 100644 --- a/src/aiida/storage/sqlite_dos/backend.py +++ b/src/aiida/storage/sqlite_dos/backend.py @@ -26,7 +26,7 @@ from aiida.common import exceptions from aiida.common.log import AIIDA_LOGGER from aiida.manage.configuration.profile import Profile -from aiida.manage.configuration.settings import AiiDAConfigPathResolver +from aiida.manage.configuration.settings import AiiDAConfigDir from aiida.orm.implementation import BackendEntity from aiida.storage.log import MIGRATE_LOGGER from aiida.storage.psql_dos.models.settings import DbSetting @@ -204,7 +204,7 @@ class Model(BaseModel, defer_build=True): title='Directory of the backend', description='Filepath of the directory in which to store data for this backend.', default_factory=lambda: str( - AiiDAConfigPathResolver.get_configuration_directory() / 'repository' / f'sqlite_dos_{uuid4().hex}' + AiiDAConfigDir.get_configuration_directory() / 'repository' / f'sqlite_dos_{uuid4().hex}' ), ) diff --git a/src/aiida/tools/pytest_fixtures/configuration.py b/src/aiida/tools/pytest_fixtures/configuration.py index cb82be05a9..7efd22265e 100644 --- a/src/aiida/tools/pytest_fixtures/configuration.py +++ b/src/aiida/tools/pytest_fixtures/configuration.py @@ -10,7 +10,7 @@ import pytest -from aiida.manage.configuration.settings import AiiDAConfigPathResolver +from aiida.manage.configuration.settings import AiiDAConfigDir if t.TYPE_CHECKING: from aiida.manage.configuration.config import Config @@ -55,7 +55,7 @@ def factory(dirpath: pathlib.Path): dirpath_config = dirpath / settings.DEFAULT_CONFIG_DIR_NAME os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = str(dirpath_config) - AiiDAConfigPathResolver.set_configuration_directory(dirpath_config) + AiiDAConfigDir.set_configuration_directory(dirpath_config) config = get_config(create=True) try: @@ -63,7 +63,7 @@ def factory(dirpath: pathlib.Path): finally: if current_config: reset_config() - AiiDAConfigPathResolver.set_configuration_directory(pathlib.Path(current_config.dirpath)) + AiiDAConfigDir.set_configuration_directory(pathlib.Path(current_config.dirpath)) get_config() if current_path_variable is None: diff --git a/tests/conftest.py b/tests/conftest.py index fd574ec15a..651c9eec25 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -30,7 +30,7 @@ from aiida.common.folders import Folder from aiida.common.links import LinkType from aiida.manage.configuration import Profile, get_config, load_profile -from aiida.manage.configuration.settings import AiiDAConfigPathResolver +from aiida.manage.configuration.settings import AiiDAConfigDir if t.TYPE_CHECKING: from aiida.manage.configuration.config import Config @@ -343,7 +343,7 @@ def empty_config(tmp_path) -> Config: # Set the configuration directory to a temporary directory. This will create the necessary folders for an empty # AiiDA configuration and set relevant global variables in :mod:`aiida.manage.configuration.settings`. - AiiDAConfigPathResolver.set_configuration_directory(tmp_path) + AiiDAConfigDir.set_configuration_directory(tmp_path) # The constructor of `Config` called by `load_config` will print warning messages about migrating it with Capturing(): @@ -361,7 +361,7 @@ def empty_config(tmp_path) -> Config: # like the :class:`aiida.engine.daemon.client.DaemonClient` will not function properly after a test that uses # this fixture because the paths of the daemon files would still point to the path of the temporary config # folder created by this fixture. - AiiDAConfigPathResolver.set_configuration_directory(pathlib.Path(current_config_path)) + AiiDAConfigDir.set_configuration_directory(pathlib.Path(current_config_path)) # Reload the original profile manager.load_profile(current_profile_name) diff --git a/tests/manage/configuration/test_config.py b/tests/manage/configuration/test_config.py index 667ee0643b..2af16e3ec8 100644 --- a/tests/manage/configuration/test_config.py +++ b/tests/manage/configuration/test_config.py @@ -19,7 +19,7 @@ from aiida.manage.configuration.config import Config from aiida.manage.configuration.migrations import CURRENT_CONFIG_VERSION, OLDEST_COMPATIBLE_CONFIG_VERSION from aiida.manage.configuration.options import get_option -from aiida.manage.configuration.settings import AiiDAConfigPathResolver +from aiida.manage.configuration.settings import AiiDAConfigDir from aiida.storage.sqlite_temp import SqliteTempBackend @@ -43,7 +43,7 @@ def cache_aiida_path_variable(): # Make sure to reset the global variables set by the following call that are dependent on the environment variable # ``DEFAULT_AIIDA_PATH_VARIABLE``. It may have been changed by a test using this fixture. - AiiDAConfigPathResolver.set_configuration_directory() + AiiDAConfigDir.set_configuration_directory() @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -66,11 +66,11 @@ def test_environment_variable_not_set(chdir_tmp_path, monkeypatch): del os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] except KeyError: pass - AiiDAConfigPathResolver.set_configuration_directory() + AiiDAConfigDir.set_configuration_directory() config_folder = chdir_tmp_path / settings.DEFAULT_CONFIG_DIR_NAME assert os.path.isdir(config_folder) - assert AiiDAConfigPathResolver.get_configuration_directory() == pathlib.Path(config_folder) + assert AiiDAConfigDir.get_configuration_directory() == pathlib.Path(config_folder) @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -79,12 +79,12 @@ def test_environment_variable_set_single_path_without_config_folder(tmp_path): """If `AIIDA_PATH` is set but does not contain a configuration folder, it should be created.""" # Set the environment variable and call configuration initialization os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = str(tmp_path) - AiiDAConfigPathResolver.set_configuration_directory() + AiiDAConfigDir.set_configuration_directory() # This should have created the configuration directory in the path config_folder = tmp_path / settings.DEFAULT_CONFIG_DIR_NAME assert config_folder.is_dir() - assert AiiDAConfigPathResolver.get_configuration_directory() == config_folder + assert AiiDAConfigDir.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -95,12 +95,12 @@ def test_environment_variable_set_single_path_with_config_folder(tmp_path): # Set the environment variable and call configuration initialization os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = str(tmp_path) - AiiDAConfigPathResolver.set_configuration_directory() + AiiDAConfigDir.set_configuration_directory() # This should have created the configuration directory in the path config_folder = tmp_path / settings.DEFAULT_CONFIG_DIR_NAME assert config_folder.is_dir() - assert AiiDAConfigPathResolver.get_configuration_directory() == config_folder + assert AiiDAConfigDir.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -115,12 +115,12 @@ def test_environment_variable_path_including_config_folder(tmp_path): """ # Set the environment variable with a path that include base folder name and call config initialization os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = str(tmp_path / settings.DEFAULT_CONFIG_DIR_NAME) - AiiDAConfigPathResolver.set_configuration_directory() + AiiDAConfigDir.set_configuration_directory() # This should have created the configuration directory in the pathpath config_folder = tmp_path / settings.DEFAULT_CONFIG_DIR_NAME assert config_folder.is_dir() - assert AiiDAConfigPathResolver.get_configuration_directory() == config_folder + assert AiiDAConfigDir.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -138,12 +138,12 @@ def test_environment_variable_set_multiple_path(tmp_path): # Set the environment variable to contain three paths and call configuration initialization env_variable = f'{directory_a}:{directory_b}:{directory_c}' os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = env_variable - AiiDAConfigPathResolver.set_configuration_directory() + AiiDAConfigDir.set_configuration_directory() # This should have created the configuration directory in the last path config_folder = directory_c / settings.DEFAULT_CONFIG_DIR_NAME assert os.path.isdir(config_folder) - assert AiiDAConfigPathResolver.get_configuration_directory() == config_folder + assert AiiDAConfigDir.get_configuration_directory() == config_folder def compare_config_in_memory_and_on_disk(config, filepath): diff --git a/tests/manage/test_caching_config.py b/tests/manage/test_caching_config.py index 96461d9161..681ae90350 100644 --- a/tests/manage/test_caching_config.py +++ b/tests/manage/test_caching_config.py @@ -46,7 +46,7 @@ def test_merge_deprecated_yaml(tmp_path): from aiida.common.warnings import AiidaDeprecationWarning from aiida.manage import configuration, get_manager from aiida.manage.configuration import get_config_option, load_profile - from aiida.manage.configuration.settings import AiiDAConfigPathResolver + from aiida.manage.configuration.settings import AiiDAConfigDir # Store the current configuration instance and config directory path current_config = configuration.CONFIG @@ -58,7 +58,7 @@ def test_merge_deprecated_yaml(tmp_path): configuration.CONFIG = None # Create a temporary folder, set it as the current config directory path - AiiDAConfigPathResolver.set_configuration_directory(pathlib.Path(tmp_path)) + AiiDAConfigDir.set_configuration_directory(pathlib.Path(tmp_path)) config_dictionary = json.loads( pathlib.Path(__file__) .parent.joinpath('configuration/migrations/test_samples/reference/6.json') @@ -87,7 +87,7 @@ def test_merge_deprecated_yaml(tmp_path): # Reset the config folder path and the config instance. Note this will always be executed after the yield no # matter what happened in the test that used this fixture. get_manager().unload_profile() - AiiDAConfigPathResolver.set_configuration_directory(current_config_path) + AiiDAConfigDir.set_configuration_directory(current_config_path) configuration.CONFIG = current_config load_profile(current_profile_name)