From fdff314296cc3d0b00c02836448c5eadbf3214e5 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 00:12:15 +0100 Subject: [PATCH 01/31] Use only one global var for marking config folder tree --- src/aiida/manage/configuration/settings.py | 63 ++++++++++++++-------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 168abb8879..4da8da39e2 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -12,6 +12,7 @@ import os import pathlib +from typing import final import warnings DEFAULT_UMASK = 0o0077 @@ -27,12 +28,43 @@ # Assign defaults which may be overriden in set_configuration_directory() below AIIDA_CONFIG_FOLDER: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME + DAEMON_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_DAEMON_DIR_NAME DAEMON_LOG_DIR: pathlib.Path = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME ACCESS_CONTROL_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_ACCESS_CONTROL_DIR_NAME -def create_instance_directories() -> None: +@final +class AiiDAConfigPathResolver: + """Path resolver for getting daemon dir, daemon log dir ad access control dir location. + + If `config_folder` is `None`, `~/.aiida` will be the default root config folder. + """ + + def __init__(self, config_folder: pathlib.Path | None = None) -> None: + if config_folder is None: + self._aiida_path = AIIDA_CONFIG_FOLDER + else: + self._aiida_path = config_folder + + @property + def aiida_path(self) -> pathlib.Path: + return self._aiida_path + + @property + def daemon_dir(self) -> pathlib.Path: + return self._aiida_path / DEFAULT_DAEMON_DIR_NAME + + @property + def daemon_log_dir(self) -> pathlib.Path: + return self._aiida_path / DEFAULT_DAEMON_DIR_NAME / DEFAULT_DAEMON_LOG_DIR_NAME + + @property + def access_control_dir(self) -> pathlib.Path: + return self._aiida_path / DEFAULT_ACCESS_CONTROL_DIR_NAME + + +def create_instance_directories(aiida_config_folder: pathlib.Path | None) -> None: """Create the base directories required for a new AiiDA instance. This will create the base AiiDA directory defined by the AIIDA_CONFIG_FOLDER variable, unless it already exists. @@ -40,23 +72,20 @@ def create_instance_directories() -> None: """ from aiida.common import ConfigurationError - directory_base = AIIDA_CONFIG_FOLDER.expanduser() - directory_daemon = directory_base / DAEMON_DIR - directory_daemon_log = directory_base / DAEMON_LOG_DIR - directory_access = directory_base / ACCESS_CONTROL_DIR + path_resolver = AiiDAConfigPathResolver(aiida_config_folder) list_of_paths = [ - directory_base, - directory_daemon, - directory_daemon_log, - directory_access, + path_resolver.aiida_path, + path_resolver.daemon_dir, + path_resolver.daemon_log_dir, + path_resolver.access_control_dir, ] umask = os.umask(DEFAULT_UMASK) try: for path in list_of_paths: - if path is directory_base and not path.exists(): + if path is path_resolver.aiida_path and not path.exists(): warnings.warn(f'Creating AiiDA configuration folder `{path}`.') try: @@ -64,7 +93,7 @@ def create_instance_directories() -> None: except OSError as exc: raise ConfigurationError(f'could not create the `{path}` configuration directory: {exc}') from exc finally: - os.umask(umask) + _ = os.umask(umask) def get_configuration_directory(): @@ -125,17 +154,7 @@ def set_configuration_directory(aiida_config_folder: pathlib.Path | None = None) is returned by ``get_configuration_directory``. If the directory does not exist yet, it is created, together with all its subdirectories. """ - global AIIDA_CONFIG_FOLDER # noqa: PLW0603 - global DAEMON_DIR # noqa: PLW0603 - global DAEMON_LOG_DIR # noqa: PLW0603 - global ACCESS_CONTROL_DIR # noqa: PLW0603 - - AIIDA_CONFIG_FOLDER = aiida_config_folder or get_configuration_directory() - DAEMON_DIR = AIIDA_CONFIG_FOLDER / DEFAULT_DAEMON_DIR_NAME - DAEMON_LOG_DIR = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME - ACCESS_CONTROL_DIR = AIIDA_CONFIG_FOLDER / DEFAULT_ACCESS_CONTROL_DIR_NAME - - create_instance_directories() + create_instance_directories(aiida_config_folder or get_configuration_directory()) # Initialize the configuration directory settings From f7398afc386a4ecd4a10aaf9fa8bcfe6babac5f9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 23:12:48 +0000 Subject: [PATCH 02/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiida/manage/configuration/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 4da8da39e2..1a1872b197 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -12,8 +12,8 @@ import os import pathlib -from typing import final import warnings +from typing import final DEFAULT_UMASK = 0o0077 DEFAULT_AIIDA_PATH_VARIABLE = 'AIIDA_PATH' From ec3ebec9bbac4957601e95ab778acb0c865ec0c6 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 00:38:45 +0100 Subject: [PATCH 03/31] part --- src/aiida/manage/configuration/profile.py | 35 ++++++++++++---------- src/aiida/manage/configuration/settings.py | 8 ++--- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index acaca2e892..f11f353204 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -10,13 +10,14 @@ from __future__ import annotations -import collections +from collections import abc import os import pathlib from copy import deepcopy -from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Type +from typing import TYPE_CHECKING, Any, Dict, Literal, Mapping, Optional, Type from aiida.common import exceptions +from aiida.manage.configuration.settings import AiiDAConfigPathResolver from .options import parse_option @@ -29,26 +30,28 @@ class Profile: """Class that models a profile as it is stored in the configuration file of an AiiDA instance.""" - KEY_UUID = 'PROFILE_UUID' - KEY_DEFAULT_USER_EMAIL = 'default_user_email' - KEY_STORAGE = 'storage' - KEY_PROCESS = 'process_control' - KEY_STORAGE_BACKEND = 'backend' - KEY_STORAGE_CONFIG = 'config' - KEY_PROCESS_BACKEND = 'backend' - KEY_PROCESS_CONFIG = 'config' - KEY_OPTIONS = 'options' - KEY_TEST_PROFILE = 'test_profile' + KEY_UUID: str = 'PROFILE_UUID' + KEY_DEFAULT_USER_EMAIL: str = 'default_user_email' + KEY_STORAGE: str = 'storage' + KEY_PROCESS: str = 'process_control' + KEY_STORAGE_BACKEND: str = 'backend' + KEY_STORAGE_CONFIG: str = 'config' + KEY_PROCESS_BACKEND: str = 'backend' + KEY_PROCESS_CONFIG: str = 'config' + KEY_OPTIONS: str = 'options' + KEY_TEST_PROFILE: str = 'test_profile' # keys that are expected to be in the parsed configuration - REQUIRED_KEYS = ( + REQUIRED_KEYS: tuple[Literal['storage'], Literal['process_control']] = ( KEY_STORAGE, KEY_PROCESS, ) - def __init__(self, name: str, config: Mapping[str, Any], validate=True): + def __init__( + self, name: str, config: Mapping[str, Any], config_folder: pathlib.Path | None = None, validate: bool = True + ): """Load a profile with the profile configuration.""" - if not isinstance(config, collections.abc.Mapping): + if not isinstance(config, abc.Mapping): raise TypeError(f'config should be a mapping but is {type(config)}') if validate and not set(config.keys()).issuperset(self.REQUIRED_KEYS): raise exceptions.ConfigurationError( @@ -64,6 +67,8 @@ def __init__(self, name: str, config: Mapping[str, Any], validate=True): self._attributes[self.KEY_UUID] = uuid4().hex + self._config_path_resolver: AiiDAConfigPathResolver = AiiDAConfigPathResolver(config_folder) + def __repr__(self) -> str: return f'Profile' diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 1a1872b197..bb1665aa49 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -29,10 +29,10 @@ # Assign defaults which may be overriden in set_configuration_directory() below AIIDA_CONFIG_FOLDER: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME -DAEMON_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_DAEMON_DIR_NAME -DAEMON_LOG_DIR: pathlib.Path = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME -ACCESS_CONTROL_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_ACCESS_CONTROL_DIR_NAME - +# DAEMON_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_DAEMON_DIR_NAME +# DAEMON_LOG_DIR: pathlib.Path = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME +# ACCESS_CONTROL_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_ACCESS_CONTROL_DIR_NAME +# @final class AiiDAConfigPathResolver: From 2663d7ec64c7654eb25e7c61d4575a1d641745a5 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 00:51:31 +0100 Subject: [PATCH 04/31] with global fix --- src/aiida/manage/configuration/settings.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index bb1665aa49..33321a24c9 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -32,7 +32,6 @@ # DAEMON_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_DAEMON_DIR_NAME # DAEMON_LOG_DIR: pathlib.Path = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME # ACCESS_CONTROL_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_ACCESS_CONTROL_DIR_NAME -# @final class AiiDAConfigPathResolver: @@ -154,7 +153,10 @@ def set_configuration_directory(aiida_config_folder: pathlib.Path | None = None) is returned by ``get_configuration_directory``. If the directory does not exist yet, it is created, together with all its subdirectories. """ - create_instance_directories(aiida_config_folder or get_configuration_directory()) + global AIIDA_CONFIG_FOLDER + AIIDA_CONFIG_FOLDER = aiida_config_folder or get_configuration_directory() + + create_instance_directories(AIIDA_CONFIG_FOLDER) # Initialize the configuration directory settings From 418cc2a9601d2e81640ddb57b3ee0218b513707f Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 01:24:40 +0100 Subject: [PATCH 05/31] Rename global AIIDA_CONFIG_FOLDER as lowercase var --- 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 | 18 +++++++++--------- src/aiida/manage/tests/pytest_fixtures.py | 4 ++-- src/aiida/storage/sqlite_dos/backend.py | 4 ++-- tests/manage/configuration/test_config.py | 10 +++++----- tests/manage/test_caching_config.py | 4 ++-- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/aiida/cmdline/commands/cmd_presto.py b/src/aiida/cmdline/commands/cmd_presto.py index 09d7070e7c..7f455e5c74 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 AIIDA_CONFIG_FOLDER + from aiida.manage.configuration.settings import glb_aiida_config_folder from aiida.manage.external.postgres import Postgres dbinfo = { @@ -98,7 +98,7 @@ def detect_postgres_config( 'database_name': database_name, 'database_username': database_username, 'database_password': database_password, - 'repository_uri': f'file://{AIIDA_CONFIG_FOLDER / "repository" / profile_name}', + 'repository_uri': f'file://{glb_aiida_config_folder / "repository" / profile_name}', } diff --git a/src/aiida/cmdline/commands/cmd_profile.py b/src/aiida/cmdline/commands/cmd_profile.py index 3dd21b56bf..78537842a2 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 AIIDA_CONFIG_FOLDER + from aiida.manage.configuration.settings import glb_aiida_config_folder - echo.echo_report(f'configuration folder: {AIIDA_CONFIG_FOLDER}') + echo.echo_report(f'configuration folder: {glb_aiida_config_folder}') 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 f3c32327dc..af1249fa32 100644 --- a/src/aiida/cmdline/commands/cmd_status.py +++ b/src/aiida/cmdline/commands/cmd_status.py @@ -61,13 +61,13 @@ 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 AIIDA_CONFIG_FOLDER + from aiida.manage.configuration.settings import glb_aiida_config_folder from aiida.manage.manager import get_manager exit_code = ExitCode.SUCCESS print_status(ServiceStatus.UP, 'version', f'AiiDA v{__version__}') - print_status(ServiceStatus.UP, 'config', AIIDA_CONFIG_FOLDER) + print_status(ServiceStatus.UP, 'config', glb_aiida_config_folder) manager = get_manager() diff --git a/src/aiida/cmdline/params/options/commands/setup.py b/src/aiida/cmdline/params/options/commands/setup.py index 930aa97018..10ff2412a6 100644 --- a/src/aiida/cmdline/params/options/commands/setup.py +++ b/src/aiida/cmdline/params/options/commands/setup.py @@ -66,11 +66,11 @@ def get_repository_uri_default(ctx): """ import os - from aiida.manage.configuration.settings import AIIDA_CONFIG_FOLDER + from aiida.manage.configuration.settings import glb_aiida_config_folder validate_profile_parameter(ctx) - return os.path.join(AIIDA_CONFIG_FOLDER, 'repository', ctx.params['profile'].name) + return os.path.join(glb_aiida_config_folder, 'repository', ctx.params['profile'].name) def get_quicksetup_repository_uri(ctx, param, value): diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index 7227281507..60db3153a9 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -66,9 +66,9 @@ def get_config_path(): """Returns path to .aiida configuration directory.""" - from .settings import AIIDA_CONFIG_FOLDER, DEFAULT_CONFIG_FILE_NAME + from .settings import glb_aiida_config_folder, DEFAULT_CONFIG_FILE_NAME - return os.path.join(AIIDA_CONFIG_FOLDER, DEFAULT_CONFIG_FILE_NAME) + return os.path.join(glb_aiida_config_folder, 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 33321a24c9..54411ce3ff 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -27,11 +27,11 @@ DEFAULT_ACCESS_CONTROL_DIR_NAME = 'access' # Assign defaults which may be overriden in set_configuration_directory() below -AIIDA_CONFIG_FOLDER: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME +glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME -# DAEMON_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_DAEMON_DIR_NAME +# DAEMON_DIR: pathlib.Path = glb_aiida_config_folder / DEFAULT_DAEMON_DIR_NAME # DAEMON_LOG_DIR: pathlib.Path = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME -# ACCESS_CONTROL_DIR: pathlib.Path = AIIDA_CONFIG_FOLDER / DEFAULT_ACCESS_CONTROL_DIR_NAME +# ACCESS_CONTROL_DIR: pathlib.Path = glb_aiida_config_folder / DEFAULT_ACCESS_CONTROL_DIR_NAME @final class AiiDAConfigPathResolver: @@ -42,7 +42,7 @@ class AiiDAConfigPathResolver: def __init__(self, config_folder: pathlib.Path | None = None) -> None: if config_folder is None: - self._aiida_path = AIIDA_CONFIG_FOLDER + self._aiida_path = glb_aiida_config_folder else: self._aiida_path = config_folder @@ -66,7 +66,7 @@ def access_control_dir(self) -> pathlib.Path: def create_instance_directories(aiida_config_folder: pathlib.Path | None) -> None: """Create the base directories required for a new AiiDA instance. - This will create the base AiiDA directory defined by the AIIDA_CONFIG_FOLDER variable, unless it already exists. + This will create the base AiiDA directory defined by the glb_aiida_config_folder variable, unless it already exists. Subsequently, it will create the daemon directory within it and the daemon log directory. """ from aiida.common import ConfigurationError @@ -101,7 +101,7 @@ def get_configuration_directory(): The location of the configuration directory is defined following these heuristics in order: * If the ``AIIDA_PATH`` variable is set, all the paths will be checked to see if they contain a - configuration folder. The first one to be encountered will be set as ``AIIDA_CONFIG_FOLDER``. If none of them + configuration folder. The first one to be encountered will be set as ``glb_aiida_config_folder``. If none of them contain one, the last path defined in the environment variable considered is used. * If an existing directory is still not found, the ``DEFAULT_AIIDA_PATH`` is used. @@ -153,10 +153,10 @@ def set_configuration_directory(aiida_config_folder: pathlib.Path | None = None) is returned by ``get_configuration_directory``. If the directory does not exist yet, it is created, together with all its subdirectories. """ - global AIIDA_CONFIG_FOLDER - AIIDA_CONFIG_FOLDER = aiida_config_folder or get_configuration_directory() + global glb_aiida_config_folder + glb_aiida_config_folder = aiida_config_folder or get_configuration_directory() - create_instance_directories(AIIDA_CONFIG_FOLDER) + create_instance_directories(glb_aiida_config_folder) # Initialize the configuration directory settings diff --git a/src/aiida/manage/tests/pytest_fixtures.py b/src/aiida/manage/tests/pytest_fixtures.py index 92856aff66..ddd9ef0d42 100644 --- a/src/aiida/manage/tests/pytest_fixtures.py +++ b/src/aiida/manage/tests/pytest_fixtures.py @@ -178,7 +178,7 @@ def aiida_instance( dirpath_config = tmp_path_factory.mktemp('config') os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = str(dirpath_config) - settings.AIIDA_CONFIG_FOLDER = dirpath_config + settings.glb_aiida_config_folder = dirpath_config settings.set_configuration_directory() configuration.CONFIG = configuration.load_config(create=True) @@ -191,7 +191,7 @@ def aiida_instance( else: os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = current_path_variable - settings.AIIDA_CONFIG_FOLDER = current_config_path + settings.glb_aiida_config_folder = current_config_path configuration.CONFIG = current_config if current_profile: aiida_manager.load_profile(current_profile.name, allow_switch=True) diff --git a/src/aiida/storage/sqlite_dos/backend.py b/src/aiida/storage/sqlite_dos/backend.py index 7be70f4a1c..4b48daa530 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 AIIDA_CONFIG_FOLDER +from aiida.manage.configuration.settings import glb_aiida_config_folder from aiida.orm.implementation import BackendEntity from aiida.storage.log import MIGRATE_LOGGER from aiida.storage.psql_dos.models.settings import DbSetting @@ -203,7 +203,7 @@ class Model(BaseModel, defer_build=True): filepath: str = Field( title='Directory of the backend', description='Filepath of the directory in which to store data for this backend.', - default_factory=lambda: str(AIIDA_CONFIG_FOLDER / 'repository' / f'sqlite_dos_{uuid4().hex}'), + default_factory=lambda: str(glb_aiida_config_folder / 'repository' / f'sqlite_dos_{uuid4().hex}'), ) @field_validator('filepath') diff --git a/tests/manage/configuration/test_config.py b/tests/manage/configuration/test_config.py index b1decb09a7..02f7254de0 100644 --- a/tests/manage/configuration/test_config.py +++ b/tests/manage/configuration/test_config.py @@ -69,7 +69,7 @@ def test_environment_variable_not_set(chdir_tmp_path, monkeypatch): config_folder = chdir_tmp_path / settings.DEFAULT_CONFIG_DIR_NAME assert os.path.isdir(config_folder) - assert settings.AIIDA_CONFIG_FOLDER == pathlib.Path(config_folder) + assert settings.glb_aiida_config_folder == pathlib.Path(config_folder) @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -83,7 +83,7 @@ def test_environment_variable_set_single_path_without_config_folder(tmp_path): # 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 settings.AIIDA_CONFIG_FOLDER == config_folder + assert settings.glb_aiida_config_folder == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -99,7 +99,7 @@ def test_environment_variable_set_single_path_with_config_folder(tmp_path): # 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 settings.AIIDA_CONFIG_FOLDER == config_folder + assert settings.glb_aiida_config_folder == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -119,7 +119,7 @@ def test_environment_variable_path_including_config_folder(tmp_path): # 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 settings.AIIDA_CONFIG_FOLDER == config_folder + assert settings.glb_aiida_config_folder == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -142,7 +142,7 @@ def test_environment_variable_set_multiple_path(tmp_path): # 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 settings.AIIDA_CONFIG_FOLDER == config_folder + assert settings.glb_aiida_config_folder == 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 50a2e4ac48..50f4f46038 100644 --- a/tests/manage/test_caching_config.py +++ b/tests/manage/test_caching_config.py @@ -57,7 +57,7 @@ def test_merge_deprecated_yaml(tmp_path): configuration.CONFIG = None # Create a temporary folder, set it as the current config directory path - settings.AIIDA_CONFIG_FOLDER = str(tmp_path) + settings.glb_aiida_config_folder = str(tmp_path) config_dictionary = json.loads( pathlib.Path(__file__) .parent.joinpath('configuration/migrations/test_samples/reference/6.json') @@ -86,7 +86,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() - settings.AIIDA_CONFIG_FOLDER = current_config_path + settings.glb_aiida_config_folder = current_config_path configuration.CONFIG = current_config load_profile(current_profile_name) From d7f36848412adda3c8e7874e0f87cb327b738041 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 01:37:24 +0100 Subject: [PATCH 06/31] Remove use of ACCESS_CONTROL_DIR --- src/aiida/manage/configuration/profile.py | 15 ++++++++++++--- src/aiida/manage/configuration/settings.py | 12 ++++++------ src/aiida/manage/profile_access.py | 8 +++----- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index f11f353204..48b6aef6fd 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -17,6 +17,7 @@ from typing import TYPE_CHECKING, Any, Dict, Literal, Mapping, Optional, Type from aiida.common import exceptions +from aiida.common.lang import type_check from aiida.manage.configuration.settings import AiiDAConfigPathResolver from .options import parse_option @@ -51,14 +52,13 @@ def __init__( self, name: str, config: Mapping[str, Any], config_folder: pathlib.Path | None = None, validate: bool = True ): """Load a profile with the profile configuration.""" - if not isinstance(config, abc.Mapping): - raise TypeError(f'config should be a mapping but is {type(config)}') + _ = type_check(config, abc.Mapping) if validate and not set(config.keys()).issuperset(self.REQUIRED_KEYS): raise exceptions.ConfigurationError( f'profile {name!r} configuration does not contain all required keys: {self.REQUIRED_KEYS}' ) - self._name = name + self._name: str = name self._attributes: Dict[str, Any] = deepcopy(config) # Create a default UUID if not specified @@ -88,6 +88,15 @@ def uuid(self) -> str: def uuid(self, value: str) -> None: self._attributes[self.KEY_UUID] = value + @property + def config_path_resolver(self) -> AiiDAConfigPathResolver: + """The config_path_resolver property.""" + return self._config_path_resolver + + @config_path_resolver.setter + def config_path_resolver(self, value): + self._config_path_resolver = value + @property def default_user_email(self) -> Optional[str]: """Return the default user email.""" diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 54411ce3ff..e0e0277733 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -29,9 +29,9 @@ # Assign defaults which may be overriden in set_configuration_directory() below glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME -# DAEMON_DIR: pathlib.Path = glb_aiida_config_folder / DEFAULT_DAEMON_DIR_NAME -# DAEMON_LOG_DIR: pathlib.Path = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME -# ACCESS_CONTROL_DIR: pathlib.Path = glb_aiida_config_folder / DEFAULT_ACCESS_CONTROL_DIR_NAME +DAEMON_DIR: pathlib.Path = glb_aiida_config_folder / DEFAULT_DAEMON_DIR_NAME +DAEMON_LOG_DIR: pathlib.Path = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME + @final class AiiDAConfigPathResolver: @@ -101,8 +101,8 @@ def get_configuration_directory(): The location of the configuration directory is defined following these heuristics in order: * If the ``AIIDA_PATH`` variable is set, all the paths will be checked to see if they contain a - configuration folder. The first one to be encountered will be set as ``glb_aiida_config_folder``. If none of them - contain one, the last path defined in the environment variable considered is used. + configuration folder. The first one to be encountered will be set as ``glb_aiida_config_folder``. + If none of them contain one, the last path defined in the environment variable considered is used. * If an existing directory is still not found, the ``DEFAULT_AIIDA_PATH`` is used. :returns: The path of the configuration directory. @@ -153,7 +153,7 @@ def set_configuration_directory(aiida_config_folder: pathlib.Path | None = None) is returned by ``get_configuration_directory``. If the directory does not exist yet, it is created, together with all its subdirectories. """ - global glb_aiida_config_folder + global glb_aiida_config_folder # noqa: PLW0603 glb_aiida_config_folder = aiida_config_folder or get_configuration_directory() create_instance_directories(glb_aiida_config_folder) diff --git a/src/aiida/manage/profile_access.py b/src/aiida/manage/profile_access.py index 5b04481e66..01aa556cf8 100644 --- a/src/aiida/manage/profile_access.py +++ b/src/aiida/manage/profile_access.py @@ -19,7 +19,7 @@ from aiida.common.lang import type_check from aiida.manage.configuration import Profile - +@typing.final class ProfileAccessManager: """Class to manage access to a profile. @@ -45,12 +45,10 @@ def __init__(self, profile: Profile): :param profile: the profile whose access to manage. """ - from aiida.manage.configuration.settings import ACCESS_CONTROL_DIR - - type_check(profile, Profile) + _ = type_check(profile, Profile) self.profile = profile self.process = psutil.Process(os.getpid()) - self._dirpath_records = ACCESS_CONTROL_DIR / profile.name + self._dirpath_records = profile.config_path_resolver.access_control_dir / profile.name self._dirpath_records.mkdir(exist_ok=True) def request_access(self) -> None: From 959a9ad8263c4e6e0d2a087bf95af34960618974 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 01:42:55 +0100 Subject: [PATCH 07/31] Remove use of DAEMON_DIR --- src/aiida/manage/configuration/profile.py | 15 ++++++++------- src/aiida/manage/configuration/settings.py | 3 --- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index 48b6aef6fd..61819fbe6b 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -249,22 +249,23 @@ def filepaths(self): :return: a dictionary of filepaths """ - from .settings import DAEMON_DIR, DAEMON_LOG_DIR + daemon_dir = self._config_path_resolver.daemon_dir + daemon_log_dir = self._config_path_resolver.daemon_log_dir return { 'circus': { - 'log': str(DAEMON_LOG_DIR / f'circus-{self.name}.log'), - 'pid': str(DAEMON_DIR / f'circus-{self.name}.pid'), - 'port': str(DAEMON_DIR / f'circus-{self.name}.port'), + 'log': str(daemon_log_dir / f'circus-{self.name}.log'), + 'pid': str(daemon_dir / f'circus-{self.name}.pid'), + 'port': str(daemon_dir / f'circus-{self.name}.port'), 'socket': { - 'file': str(DAEMON_DIR / f'circus-{self.name}.sockets'), + 'file': str(daemon_dir / f'circus-{self.name}.sockets'), 'controller': 'circus.c.sock', 'pubsub': 'circus.p.sock', 'stats': 'circus.s.sock', }, }, 'daemon': { - 'log': str(DAEMON_LOG_DIR / f'aiida-{self.name}.log'), - 'pid': str(DAEMON_DIR / f'aiida-{self.name}.pid'), + 'log': str(daemon_log_dir / f'aiida-{self.name}.log'), + 'pid': str(daemon_dir / f'aiida-{self.name}.pid'), }, } diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index e0e0277733..08ad54478f 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -29,9 +29,6 @@ # Assign defaults which may be overriden in set_configuration_directory() below glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME -DAEMON_DIR: pathlib.Path = glb_aiida_config_folder / DEFAULT_DAEMON_DIR_NAME -DAEMON_LOG_DIR: pathlib.Path = DAEMON_DIR / DEFAULT_DAEMON_LOG_DIR_NAME - @final class AiiDAConfigPathResolver: From 717cf06122e4293f2cd5257e4714263d84278a53 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 00:42:59 +0000 Subject: [PATCH 08/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiida/manage/configuration/__init__.py | 2 +- src/aiida/manage/configuration/profile.py | 2 +- src/aiida/manage/profile_access.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index 60db3153a9..f9151d30ff 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -66,7 +66,7 @@ def get_config_path(): """Returns path to .aiida configuration directory.""" - from .settings import glb_aiida_config_folder, DEFAULT_CONFIG_FILE_NAME + from .settings import DEFAULT_CONFIG_FILE_NAME, glb_aiida_config_folder return os.path.join(glb_aiida_config_folder, DEFAULT_CONFIG_FILE_NAME) diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index 61819fbe6b..5d56a94201 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -10,9 +10,9 @@ from __future__ import annotations -from collections import abc import os import pathlib +from collections import abc from copy import deepcopy from typing import TYPE_CHECKING, Any, Dict, Literal, Mapping, Optional, Type diff --git a/src/aiida/manage/profile_access.py b/src/aiida/manage/profile_access.py index 01aa556cf8..8602539e13 100644 --- a/src/aiida/manage/profile_access.py +++ b/src/aiida/manage/profile_access.py @@ -19,6 +19,7 @@ from aiida.common.lang import type_check from aiida.manage.configuration import Profile + @typing.final class ProfileAccessManager: """Class to manage access to a profile. From 5ed4950c80335e3a7f3927988105340b3dbcb532 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 01:44:14 +0100 Subject: [PATCH 09/31] Unbounded warning fix --- src/aiida/manage/configuration/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 08ad54478f..e9fc0ce576 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -127,6 +127,8 @@ def get_configuration_directory_from_envvar() -> pathlib.Path | None: if environment_variable is None: return None + dirpath_config = None + # Loop over all the paths in the ``AIIDA_PATH`` variable to see if any of them contain a configuration folder for base_dir_path in [path for path in environment_variable.split(':') if path]: dirpath_config = pathlib.Path(base_dir_path).expanduser() From 1b260441d7f14c684c5f6a54445c5c470550eee2 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 02:17:29 +0100 Subject: [PATCH 10/31] src/aiida/manage/configuration/profile.py out of mypy exclude list --- .pre-commit-config.yaml | 1 - src/aiida/common/lang.py | 4 +++- src/aiida/manage/configuration/profile.py | 28 ++++++++++------------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 32305828b4..084d4b3423 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -124,7 +124,6 @@ repos: src/aiida/engine/processes/ports.py| src/aiida/manage/configuration/__init__.py| src/aiida/manage/configuration/config.py| - src/aiida/manage/configuration/profile.py| src/aiida/manage/external/rmq/launcher.py| src/aiida/manage/tests/main.py| src/aiida/manage/tests/pytest_fixtures.py| diff --git a/src/aiida/common/lang.py b/src/aiida/common/lang.py index ec9fb45ddb..251db3e6fd 100644 --- a/src/aiida/common/lang.py +++ b/src/aiida/common/lang.py @@ -23,7 +23,9 @@ def isidentifier(identifier): return identifier.isidentifier() and not keyword.iskeyword(identifier) -def type_check(what, of_type, msg=None, allow_none=False): +def type_check( + what: object, of_type, msg: str | None = None, allow_none: bool = False +) -> object | None: """Verify that object 'what' is of type 'of_type' and if not the case, raise a TypeError. :param what: the object to check diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index 5d56a94201..875d89b4e2 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -14,7 +14,7 @@ import pathlib from collections import abc from copy import deepcopy -from typing import TYPE_CHECKING, Any, Dict, Literal, Mapping, Optional, Type +from typing import TYPE_CHECKING, Any, Literal, cast from aiida.common import exceptions from aiida.common.lang import type_check @@ -43,13 +43,13 @@ class Profile: KEY_TEST_PROFILE: str = 'test_profile' # keys that are expected to be in the parsed configuration - REQUIRED_KEYS: tuple[Literal['storage'], Literal['process_control']] = ( + REQUIRED_KEYS: tuple[str, str] = ( KEY_STORAGE, KEY_PROCESS, ) def __init__( - self, name: str, config: Mapping[str, Any], config_folder: pathlib.Path | None = None, validate: bool = True + self, name: str, config: abc.Mapping[str, Any], config_folder: pathlib.Path | None = None, validate: bool = True ): """Load a profile with the profile configuration.""" _ = type_check(config, abc.Mapping) @@ -59,7 +59,7 @@ def __init__( ) self._name: str = name - self._attributes: Dict[str, Any] = deepcopy(config) + self._attributes: dict[str, Any] = cast(dict[str, Any], deepcopy(config)) # Create a default UUID if not specified if self._attributes.get(self.KEY_UUID, None) is None: @@ -93,17 +93,13 @@ def config_path_resolver(self) -> AiiDAConfigPathResolver: """The config_path_resolver property.""" return self._config_path_resolver - @config_path_resolver.setter - def config_path_resolver(self, value): - self._config_path_resolver = value - @property - def default_user_email(self) -> Optional[str]: + def default_user_email(self) -> str | None: """Return the default user email.""" return self._attributes.get(self.KEY_DEFAULT_USER_EMAIL, None) @default_user_email.setter - def default_user_email(self, value: Optional[str]) -> None: + def default_user_email(self, value: str | None) -> None: """Set the default user email.""" self._attributes[self.KEY_DEFAULT_USER_EMAIL] = value @@ -113,11 +109,11 @@ def storage_backend(self) -> str: return self._attributes[self.KEY_STORAGE][self.KEY_STORAGE_BACKEND] @property - def storage_config(self) -> Dict[str, Any]: + def storage_config(self) -> dict[str, Any]: """Return the configuration required by the storage backend.""" return self._attributes[self.KEY_STORAGE][self.KEY_STORAGE_CONFIG] - def set_storage(self, name: str, config: Dict[str, Any]) -> None: + def set_storage(self, name: str, config: dict[str, Any]) -> None: """Set the storage backend and its configuration. :param name: the name of the storage backend @@ -128,7 +124,7 @@ def set_storage(self, name: str, config: Dict[str, Any]) -> None: self._attributes[self.KEY_STORAGE][self.KEY_STORAGE_CONFIG] = config @property - def storage_cls(self) -> Type['StorageBackend']: + def storage_cls(self) -> type['StorageBackend']: """Return the storage backend class for this profile.""" from aiida.plugins import StorageFactory @@ -140,11 +136,11 @@ def process_control_backend(self) -> str | None: return self._attributes[self.KEY_PROCESS][self.KEY_PROCESS_BACKEND] @property - def process_control_config(self) -> Dict[str, Any]: + def process_control_config(self) -> dict[str, Any]: """Return the configuration required by the process control backend.""" return self._attributes[self.KEY_PROCESS][self.KEY_PROCESS_CONFIG] or {} - def set_process_controller(self, name: str, config: Dict[str, Any]) -> None: + def set_process_controller(self, name: str, config: dict[str, Any]) -> None: """Set the process control backend and its configuration. :param name: the name of the process backend @@ -189,7 +185,7 @@ def name(self): return self._name @property - def dictionary(self) -> Dict[str, Any]: + def dictionary(self) -> dict[str, Any]: """Return the profile attributes as a dictionary with keys as it is stored in the config :return: the profile configuration dictionary From c4adbec90340c3fdd3bee06659e79e56472a344b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 01:36:29 +0000 Subject: [PATCH 11/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiida/common/lang.py | 4 +--- src/aiida/manage/configuration/profile.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/aiida/common/lang.py b/src/aiida/common/lang.py index 251db3e6fd..ec9fb45ddb 100644 --- a/src/aiida/common/lang.py +++ b/src/aiida/common/lang.py @@ -23,9 +23,7 @@ def isidentifier(identifier): return identifier.isidentifier() and not keyword.iskeyword(identifier) -def type_check( - what: object, of_type, msg: str | None = None, allow_none: bool = False -) -> object | None: +def type_check(what, of_type, msg=None, allow_none=False): """Verify that object 'what' is of type 'of_type' and if not the case, raise a TypeError. :param what: the object to check diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index 875d89b4e2..d1d586f8b3 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -14,7 +14,7 @@ import pathlib from collections import abc from copy import deepcopy -from typing import TYPE_CHECKING, Any, Literal, cast +from typing import TYPE_CHECKING, Any, cast from aiida.common import exceptions from aiida.common.lang import type_check From 8795935a064c9fbcbe995c63f6ba543147ede8fd Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 18:16:02 +0100 Subject: [PATCH 12/31] Update src/aiida/manage/configuration/settings.py Co-authored-by: Julian Geiger --- src/aiida/manage/configuration/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index e9fc0ce576..ea4e569907 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -32,7 +32,7 @@ @final class AiiDAConfigPathResolver: - """Path resolver for getting daemon dir, daemon log dir ad access control dir location. + """Path resolver for getting daemon dir, daemon log dir and access control dir location. If `config_folder` is `None`, `~/.aiida` will be the default root config folder. """ From ad1086f81d65899c3356ddbed04701ffa8ee3d50 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 15 Nov 2024 18:19:00 +0100 Subject: [PATCH 13/31] fj --- src/aiida/manage/configuration/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index ea4e569907..892d98f9ba 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -34,7 +34,7 @@ class AiiDAConfigPathResolver: """Path resolver for getting daemon dir, daemon log dir and access control dir location. - If `config_folder` is `None`, `~/.aiida` will be the default root config folder. + If ``config_folder`` is ``None``, ``~/.aiida`` will be the default root config folder. """ def __init__(self, config_folder: pathlib.Path | None = None) -> None: From 95e305cb023302dd640384efeca9a8d18af63f82 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Sat, 16 Nov 2024 00:51:21 +0100 Subject: [PATCH 14/31] global object pattern applied changes applied to not allow glb var escape from module scope --- src/aiida/cmdline/commands/cmd_presto.py | 6 +- src/aiida/cmdline/commands/cmd_profile.py | 4 +- src/aiida/cmdline/commands/cmd_status.py | 5 +- .../cmdline/params/options/commands/setup.py | 4 +- src/aiida/manage/configuration/__init__.py | 4 +- src/aiida/manage/configuration/profile.py | 6 +- src/aiida/manage/configuration/settings.py | 60 +++++++------------ src/aiida/manage/tests/pytest_fixtures.py | 5 +- src/aiida/storage/sqlite_dos/backend.py | 4 +- tests/manage/configuration/test_config.py | 10 ++-- tests/manage/test_caching_config.py | 6 +- 11 files changed, 49 insertions(+), 65 deletions(-) diff --git a/src/aiida/cmdline/commands/cmd_presto.py b/src/aiida/cmdline/commands/cmd_presto.py index 7f455e5c74..c7ca64ab5a 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 glb_aiida_config_folder + from aiida.manage.configuration.settings import get_configuration_directory from aiida.manage.external.postgres import Postgres dbinfo = { @@ -92,13 +92,15 @@ def detect_postgres_config( except Exception as exception: raise ConnectionError(f'Unable to automatically create the PostgreSQL user and database: {exception}') + aiida_config_folder = get_configuration_directory() + return { 'database_hostname': postgres_hostname, 'database_port': postgres_port, 'database_name': database_name, 'database_username': database_username, 'database_password': database_password, - 'repository_uri': f'file://{glb_aiida_config_folder / "repository" / profile_name}', + 'repository_uri': f'file://{aiida_config_folder / "repository" / profile_name}', } diff --git a/src/aiida/cmdline/commands/cmd_profile.py b/src/aiida/cmdline/commands/cmd_profile.py index 78537842a2..5fdd2f9944 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 glb_aiida_config_folder + from aiida.manage.configuration.settings import get_configuration_directory - echo.echo_report(f'configuration folder: {glb_aiida_config_folder}') + echo.echo_report(f'configuration folder: {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 af1249fa32..0186f473f9 100644 --- a/src/aiida/cmdline/commands/cmd_status.py +++ b/src/aiida/cmdline/commands/cmd_status.py @@ -61,13 +61,14 @@ 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 glb_aiida_config_folder + from aiida.manage.configuration.settings import get_configuration_directory from aiida.manage.manager import get_manager exit_code = ExitCode.SUCCESS + configure_directory = get_configuration_directory() print_status(ServiceStatus.UP, 'version', f'AiiDA v{__version__}') - print_status(ServiceStatus.UP, 'config', glb_aiida_config_folder) + print_status(ServiceStatus.UP, 'config', str(configure_directory)) manager = get_manager() diff --git a/src/aiida/cmdline/params/options/commands/setup.py b/src/aiida/cmdline/params/options/commands/setup.py index 10ff2412a6..fc4f031aa3 100644 --- a/src/aiida/cmdline/params/options/commands/setup.py +++ b/src/aiida/cmdline/params/options/commands/setup.py @@ -66,11 +66,11 @@ def get_repository_uri_default(ctx): """ import os - from aiida.manage.configuration.settings import glb_aiida_config_folder + from aiida.manage.configuration.settings import get_configuration_directory validate_profile_parameter(ctx) - return os.path.join(glb_aiida_config_folder, 'repository', ctx.params['profile'].name) + return os.path.join(get_configuration_directory(), 'repository', ctx.params['profile'].name) def get_quicksetup_repository_uri(ctx, param, value): diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index f9151d30ff..d1f3b07de5 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -66,9 +66,9 @@ def get_config_path(): """Returns path to .aiida configuration directory.""" - from .settings import DEFAULT_CONFIG_FILE_NAME, glb_aiida_config_folder + from .settings import DEFAULT_CONFIG_FILE_NAME, get_configuration_directory - return os.path.join(glb_aiida_config_folder, DEFAULT_CONFIG_FILE_NAME) + return os.path.join(str(get_configuration_directory()), DEFAULT_CONFIG_FILE_NAME) def load_config(create=False) -> 'Config': diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index d1d586f8b3..bd917f3130 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -18,7 +18,7 @@ from aiida.common import exceptions from aiida.common.lang import type_check -from aiida.manage.configuration.settings import AiiDAConfigPathResolver +from aiida.manage.configuration.settings import AiiDAConfigPathResolver, get_configuration_directory from .options import parse_option @@ -67,7 +67,9 @@ def __init__( self._attributes[self.KEY_UUID] = uuid4().hex - self._config_path_resolver: AiiDAConfigPathResolver = AiiDAConfigPathResolver(config_folder) + self._config_path_resolver: AiiDAConfigPathResolver = AiiDAConfigPathResolver( + config_folder or get_configuration_directory() + ) def __repr__(self) -> str: return f'Profile' diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 892d98f9ba..f4eb85b274 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -27,21 +27,15 @@ DEFAULT_ACCESS_CONTROL_DIR_NAME = 'access' # Assign defaults which may be overriden in set_configuration_directory() below -glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME +_glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME @final class AiiDAConfigPathResolver: - """Path resolver for getting daemon dir, daemon log dir and access control dir location. + """Path resolver for getting daemon dir, daemon log dir and access control dir location.""" - If ``config_folder`` is ``None``, ``~/.aiida`` will be the default root config folder. - """ - - def __init__(self, config_folder: pathlib.Path | None = None) -> None: - if config_folder is None: - self._aiida_path = glb_aiida_config_folder - else: - self._aiida_path = config_folder + def __init__(self, config_folder: pathlib.Path) -> None: + self._aiida_path = config_folder @property def aiida_path(self) -> pathlib.Path: @@ -63,12 +57,13 @@ def access_control_dir(self) -> pathlib.Path: def create_instance_directories(aiida_config_folder: pathlib.Path | None) -> None: """Create the base directories required for a new AiiDA instance. - This will create the base AiiDA directory defined by the glb_aiida_config_folder variable, unless it already exists. + This will create the base AiiDA directory defined by the ``aiida_config_folder`` if not provided + ``_glb_aiida_config_folder`` will be the default config folder, unless it already exists. Subsequently, it will create the daemon directory within it and the daemon log directory. """ from aiida.common import ConfigurationError - path_resolver = AiiDAConfigPathResolver(aiida_config_folder) + path_resolver = AiiDAConfigPathResolver(aiida_config_folder or _glb_aiida_config_folder) list_of_paths = [ path_resolver.aiida_path, @@ -93,27 +88,11 @@ def create_instance_directories(aiida_config_folder: pathlib.Path | None) -> Non def get_configuration_directory(): - """Return the path of the configuration directory. - - The location of the configuration directory is defined following these heuristics in order: - - * If the ``AIIDA_PATH`` variable is set, all the paths will be checked to see if they contain a - configuration folder. The first one to be encountered will be set as ``glb_aiida_config_folder``. - If none of them contain one, the last path defined in the environment variable considered is used. - * If an existing directory is still not found, the ``DEFAULT_AIIDA_PATH`` is used. - - :returns: The path of the configuration directory. - """ - dirpath_config = get_configuration_directory_from_envvar() + """Return the path of the configuration directory.""" + return _glb_aiida_config_folder - # If no existing configuration directory is found, fall back to the default - if dirpath_config is None: - dirpath_config = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME - return dirpath_config - - -def get_configuration_directory_from_envvar() -> pathlib.Path | None: +def get_configuration_directory_from_envvar() -> pathlib.Path: """Return the path of a config directory from the ``AIIDA_PATH`` environment variable. The environment variable should be a colon separated string of filepaths that either point directly to a config @@ -124,12 +103,13 @@ def get_configuration_directory_from_envvar() -> pathlib.Path | None: """ environment_variable = os.environ.get(DEFAULT_AIIDA_PATH_VARIABLE) - if environment_variable is None: - return None + default_dirpath_config = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME - dirpath_config = None + if environment_variable is None: + return default_dirpath_config # Loop over all the paths in the ``AIIDA_PATH`` variable to see if any of them contain a configuration folder + dirpath_config = None for base_dir_path in [path for path in environment_variable.split(':') if path]: dirpath_config = pathlib.Path(base_dir_path).expanduser() @@ -142,20 +122,20 @@ def get_configuration_directory_from_envvar() -> pathlib.Path | None: if dirpath_config.is_dir(): break - return dirpath_config + return dirpath_config or default_dirpath_config def set_configuration_directory(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``. If the directory does not exist yet, it is created, together with - all its subdirectories. + is returned by ``get_configuration_directory_from_envvar``. If the directory does not exist yet, it is created, + together with all its subdirectories. """ - global glb_aiida_config_folder # noqa: PLW0603 - glb_aiida_config_folder = aiida_config_folder or get_configuration_directory() + global _glb_aiida_config_folder # noqa: PLW0603 + _glb_aiida_config_folder = aiida_config_folder or get_configuration_directory_from_envvar() - create_instance_directories(glb_aiida_config_folder) + create_instance_directories(_glb_aiida_config_folder) # Initialize the configuration directory settings diff --git a/src/aiida/manage/tests/pytest_fixtures.py b/src/aiida/manage/tests/pytest_fixtures.py index ddd9ef0d42..09818429cd 100644 --- a/src/aiida/manage/tests/pytest_fixtures.py +++ b/src/aiida/manage/tests/pytest_fixtures.py @@ -178,8 +178,7 @@ def aiida_instance( dirpath_config = tmp_path_factory.mktemp('config') os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = str(dirpath_config) - settings.glb_aiida_config_folder = dirpath_config - settings.set_configuration_directory() + settings.set_configuration_directory(dirpath_config) configuration.CONFIG = configuration.load_config(create=True) try: @@ -191,7 +190,7 @@ def aiida_instance( else: os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = current_path_variable - settings.glb_aiida_config_folder = current_config_path + settings.set_configuration_directory(current_config_path) configuration.CONFIG = current_config if current_profile: aiida_manager.load_profile(current_profile.name, allow_switch=True) diff --git a/src/aiida/storage/sqlite_dos/backend.py b/src/aiida/storage/sqlite_dos/backend.py index 4b48daa530..d4ff722fd4 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 glb_aiida_config_folder +from aiida.manage.configuration.settings import get_configuration_directory from aiida.orm.implementation import BackendEntity from aiida.storage.log import MIGRATE_LOGGER from aiida.storage.psql_dos.models.settings import DbSetting @@ -203,7 +203,7 @@ class Model(BaseModel, defer_build=True): filepath: str = Field( title='Directory of the backend', description='Filepath of the directory in which to store data for this backend.', - default_factory=lambda: str(glb_aiida_config_folder / 'repository' / f'sqlite_dos_{uuid4().hex}'), + default_factory=lambda: str(get_configuration_directory() / 'repository' / f'sqlite_dos_{uuid4().hex}'), ) @field_validator('filepath') diff --git a/tests/manage/configuration/test_config.py b/tests/manage/configuration/test_config.py index 02f7254de0..d682f78dbd 100644 --- a/tests/manage/configuration/test_config.py +++ b/tests/manage/configuration/test_config.py @@ -69,7 +69,7 @@ def test_environment_variable_not_set(chdir_tmp_path, monkeypatch): config_folder = chdir_tmp_path / settings.DEFAULT_CONFIG_DIR_NAME assert os.path.isdir(config_folder) - assert settings.glb_aiida_config_folder == pathlib.Path(config_folder) + assert settings.get_configuration_directory() == pathlib.Path(config_folder) @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -83,7 +83,7 @@ def test_environment_variable_set_single_path_without_config_folder(tmp_path): # 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 settings.glb_aiida_config_folder == config_folder + assert settings.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -99,7 +99,7 @@ def test_environment_variable_set_single_path_with_config_folder(tmp_path): # 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 settings.glb_aiida_config_folder == config_folder + assert settings.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -119,7 +119,7 @@ def test_environment_variable_path_including_config_folder(tmp_path): # 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 settings.glb_aiida_config_folder == config_folder + assert settings.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -142,7 +142,7 @@ def test_environment_variable_set_multiple_path(tmp_path): # 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 settings.glb_aiida_config_folder == config_folder + assert settings.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 50f4f46038..5f3b5126eb 100644 --- a/tests/manage/test_caching_config.py +++ b/tests/manage/test_caching_config.py @@ -49,7 +49,7 @@ def test_merge_deprecated_yaml(tmp_path): # Store the current configuration instance and config directory path current_config = configuration.CONFIG - current_config_path = current_config.dirpath + current_config_path = pathlib.Path(current_config.dirpath) current_profile_name = configuration.get_profile().name try: @@ -57,7 +57,7 @@ def test_merge_deprecated_yaml(tmp_path): configuration.CONFIG = None # Create a temporary folder, set it as the current config directory path - settings.glb_aiida_config_folder = str(tmp_path) + settings.set_configuration_directory(pathlib.Path(tmp_path)) config_dictionary = json.loads( pathlib.Path(__file__) .parent.joinpath('configuration/migrations/test_samples/reference/6.json') @@ -86,7 +86,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() - settings.glb_aiida_config_folder = current_config_path + settings.set_configuration_directory(current_config_path) configuration.CONFIG = current_config load_profile(current_profile_name) From dd7f04f78b3268f9f1b3a0e0eb2adc00ec11daed Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Tue, 19 Nov 2024 22:41:12 +0100 Subject: [PATCH 15/31] Update src/aiida/manage/configuration/__init__.py --- src/aiida/manage/configuration/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index d1f3b07de5..d002ca9b86 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -68,7 +68,7 @@ def get_config_path(): """Returns path to .aiida configuration directory.""" from .settings import DEFAULT_CONFIG_FILE_NAME, get_configuration_directory - return os.path.join(str(get_configuration_directory()), DEFAULT_CONFIG_FILE_NAME) + return os.path.join(get_configuration_directory(), DEFAULT_CONFIG_FILE_NAME) def load_config(create=False) -> 'Config': From 2ce9080aefc7d572441884a65a0de686569b5ada Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Tue, 19 Nov 2024 23:49:34 +0100 Subject: [PATCH 16/31] hehe, this looks like real Singleton --- src/aiida/manage/configuration/__init__.py | 6 ++- src/aiida/manage/configuration/profile.py | 6 +-- src/aiida/manage/configuration/settings.py | 53 ++++++++++--------- src/aiida/storage/sqlite_dos/backend.py | 4 +- .../tools/pytest_fixtures/configuration.py | 6 ++- tests/conftest.py | 6 +-- tests/manage/configuration/test_config.py | 27 +++++----- 7 files changed, 55 insertions(+), 53 deletions(-) diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index d002ca9b86..ad64a7bb67 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -11,6 +11,8 @@ from __future__ import annotations +from aiida.manage.configuration.settings import AiiDAConfigPathResolver + # AUTO-GENERATED # fmt: off from .migrations import * @@ -66,9 +68,9 @@ def get_config_path(): """Returns path to .aiida configuration directory.""" - from .settings import DEFAULT_CONFIG_FILE_NAME, get_configuration_directory + from .settings import DEFAULT_CONFIG_FILE_NAME - return os.path.join(get_configuration_directory(), DEFAULT_CONFIG_FILE_NAME) + return os.path.join(AiiDAConfigPathResolver.get_configuration_directory(), DEFAULT_CONFIG_FILE_NAME) def load_config(create=False) -> 'Config': diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index bd917f3130..d1d586f8b3 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -18,7 +18,7 @@ from aiida.common import exceptions from aiida.common.lang import type_check -from aiida.manage.configuration.settings import AiiDAConfigPathResolver, get_configuration_directory +from aiida.manage.configuration.settings import AiiDAConfigPathResolver from .options import parse_option @@ -67,9 +67,7 @@ def __init__( self._attributes[self.KEY_UUID] = uuid4().hex - self._config_path_resolver: AiiDAConfigPathResolver = AiiDAConfigPathResolver( - config_folder or get_configuration_directory() - ) + self._config_path_resolver: AiiDAConfigPathResolver = AiiDAConfigPathResolver(config_folder) def __repr__(self) -> str: return f'Profile' diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index f4eb85b274..22be2be4ea 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -26,16 +26,17 @@ DEFAULT_DAEMON_LOG_DIR_NAME = 'log' DEFAULT_ACCESS_CONTROL_DIR_NAME = 'access' -# Assign defaults which may be overriden in set_configuration_directory() below -_glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME +__all__ = ('AiiDAConfigPathResolver',) @final class AiiDAConfigPathResolver: """Path resolver for getting daemon dir, daemon log dir and access control dir location.""" - def __init__(self, config_folder: pathlib.Path) -> None: - self._aiida_path = config_folder + _glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME + + def __init__(self, config_folder: pathlib.Path | None) -> None: + self._aiida_path = config_folder or self._glb_aiida_config_folder @property def aiida_path(self) -> pathlib.Path: @@ -53,8 +54,25 @@ 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() -def create_instance_directories(aiida_config_folder: pathlib.Path | None) -> None: + _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. This will create the base AiiDA directory defined by the ``aiida_config_folder`` if not provided @@ -63,7 +81,7 @@ def create_instance_directories(aiida_config_folder: pathlib.Path | None) -> Non """ from aiida.common import ConfigurationError - path_resolver = AiiDAConfigPathResolver(aiida_config_folder or _glb_aiida_config_folder) + path_resolver = AiiDAConfigPathResolver(aiida_config_folder) list_of_paths = [ path_resolver.aiida_path, @@ -87,12 +105,7 @@ def create_instance_directories(aiida_config_folder: pathlib.Path | None) -> Non _ = os.umask(umask) -def get_configuration_directory(): - """Return the path of the configuration directory.""" - return _glb_aiida_config_folder - - -def get_configuration_directory_from_envvar() -> pathlib.Path: +def _get_configuration_directory_from_envvar() -> pathlib.Path: """Return the path of a config directory from the ``AIIDA_PATH`` environment variable. The environment variable should be a colon separated string of filepaths that either point directly to a config @@ -125,18 +138,6 @@ def get_configuration_directory_from_envvar() -> pathlib.Path: return dirpath_config or default_dirpath_config -def set_configuration_directory(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. - """ - global _glb_aiida_config_folder # noqa: PLW0603 - _glb_aiida_config_folder = aiida_config_folder or get_configuration_directory_from_envvar() - - create_instance_directories(_glb_aiida_config_folder) - - +# XXX: Can I move this to aiida.__init__??? # Initialize the configuration directory settings -set_configuration_directory() +AiiDAConfigPathResolver.set_configuration_directory() diff --git a/src/aiida/storage/sqlite_dos/backend.py b/src/aiida/storage/sqlite_dos/backend.py index d4ff722fd4..52099f7391 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 get_configuration_directory +from aiida.manage.configuration.settings import AiiDAConfigPathResolver from aiida.orm.implementation import BackendEntity from aiida.storage.log import MIGRATE_LOGGER from aiida.storage.psql_dos.models.settings import DbSetting @@ -203,7 +203,7 @@ class Model(BaseModel, defer_build=True): filepath: str = Field( title='Directory of the backend', description='Filepath of the directory in which to store data for this backend.', - default_factory=lambda: str(get_configuration_directory() / 'repository' / f'sqlite_dos_{uuid4().hex}'), + default_factory=lambda: str(AiiDAConfigPathResolver.get_configuration_directory() / 'repository' / f'sqlite_dos_{uuid4().hex}'), ) @field_validator('filepath') diff --git a/src/aiida/tools/pytest_fixtures/configuration.py b/src/aiida/tools/pytest_fixtures/configuration.py index ca38db3fb0..cb82be05a9 100644 --- a/src/aiida/tools/pytest_fixtures/configuration.py +++ b/src/aiida/tools/pytest_fixtures/configuration.py @@ -10,6 +10,8 @@ import pytest +from aiida.manage.configuration.settings import AiiDAConfigPathResolver + if t.TYPE_CHECKING: from aiida.manage.configuration.config import Config @@ -53,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) - settings.set_configuration_directory(dirpath_config) + AiiDAConfigPathResolver.set_configuration_directory(dirpath_config) config = get_config(create=True) try: @@ -61,7 +63,7 @@ def factory(dirpath: pathlib.Path): finally: if current_config: reset_config() - settings.set_configuration_directory(pathlib.Path(current_config.dirpath)) + AiiDAConfigPathResolver.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 2166cc06c0..fd574ec15a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -30,6 +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 if t.TYPE_CHECKING: from aiida.manage.configuration.config import Config @@ -329,7 +330,6 @@ def empty_config(tmp_path) -> Config: """ from aiida.common.utils import Capturing from aiida.manage import configuration, get_manager - from aiida.manage.configuration import settings manager = get_manager() @@ -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`. - settings.set_configuration_directory(tmp_path) + AiiDAConfigPathResolver.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. - settings.set_configuration_directory(pathlib.Path(current_config_path)) + AiiDAConfigPathResolver.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 d682f78dbd..88aae97a69 100644 --- a/tests/manage/configuration/test_config.py +++ b/tests/manage/configuration/test_config.py @@ -16,6 +16,7 @@ import pytest from aiida.common import exceptions from aiida.manage.configuration import Profile, settings +from aiida.manage.configuration.settings import AiiDAConfigPathResolver 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 @@ -42,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. - settings.set_configuration_directory() + AiiDAConfigPathResolver.set_configuration_directory() @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -65,11 +66,11 @@ def test_environment_variable_not_set(chdir_tmp_path, monkeypatch): del os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] except KeyError: pass - settings.set_configuration_directory() + AiiDAConfigPathResolver.set_configuration_directory() config_folder = chdir_tmp_path / settings.DEFAULT_CONFIG_DIR_NAME assert os.path.isdir(config_folder) - assert settings.get_configuration_directory() == pathlib.Path(config_folder) + assert AiiDAConfigPathResolver.get_configuration_directory() == pathlib.Path(config_folder) @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -78,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) - settings.set_configuration_directory() + AiiDAConfigPathResolver.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 settings.get_configuration_directory() == config_folder + assert AiiDAConfigPathResolver.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -94,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) - settings.set_configuration_directory() + AiiDAConfigPathResolver.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 settings.get_configuration_directory() == config_folder + assert AiiDAConfigPathResolver.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -114,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) - settings.set_configuration_directory() + AiiDAConfigPathResolver.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 settings.get_configuration_directory() == config_folder + assert AiiDAConfigPathResolver.get_configuration_directory() == config_folder @pytest.mark.filterwarnings('ignore:Creating AiiDA configuration folder') @@ -137,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 - settings.set_configuration_directory() + AiiDAConfigPathResolver.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 settings.get_configuration_directory() == config_folder + assert AiiDAConfigPathResolver.get_configuration_directory() == config_folder def compare_config_in_memory_and_on_disk(config, filepath): @@ -152,9 +153,7 @@ def compare_config_in_memory_and_on_disk(config, filepath): :param filepath: absolute filepath to a configuration file :raises AssertionError: if content of `config` is not equal to that of file on disk """ - from aiida.manage.configuration.settings import DEFAULT_CONFIG_INDENT_SIZE - - in_memory = json.dumps(config.dictionary, indent=DEFAULT_CONFIG_INDENT_SIZE) + in_memory = json.dumps(config.dictionary, indent=settings.DEFAULT_CONFIG_INDENT_SIZE) # Read the content stored on disk with open(filepath, 'r', encoding='utf8') as handle: From 8408ef53d801e446c932519a0476dc16fa113ea3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 22:58:44 +0000 Subject: [PATCH 17/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiida/cmdline/commands/cmd_presto.py | 4 ++-- src/aiida/cmdline/commands/cmd_profile.py | 4 ++-- src/aiida/cmdline/commands/cmd_status.py | 6 +++--- src/aiida/cmdline/params/options/commands/setup.py | 5 +++-- src/aiida/storage/sqlite_dos/backend.py | 4 +++- tests/manage/configuration/test_config.py | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/aiida/cmdline/commands/cmd_presto.py b/src/aiida/cmdline/commands/cmd_presto.py index c7ca64ab5a..948a270609 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 get_configuration_directory + from aiida.manage.configuration.settings import AiiDAConfigPathResolver 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 = get_configuration_directory() + aiida_config_folder = AiiDAConfigPathResolver.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 5fdd2f9944..0073485965 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 get_configuration_directory + from aiida.manage.configuration.settings import AiiDAConfigPathResolver - echo.echo_report(f'configuration folder: {get_configuration_directory()}') + echo.echo_report(f'configuration folder: {AiiDAConfigPathResolver.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 0186f473f9..6683c194fa 100644 --- a/src/aiida/cmdline/commands/cmd_status.py +++ b/src/aiida/cmdline/commands/cmd_status.py @@ -61,14 +61,14 @@ 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 get_configuration_directory + from aiida.manage.configuration.settings import AiiDAConfigPathResolver from aiida.manage.manager import get_manager exit_code = ExitCode.SUCCESS - configure_directory = get_configuration_directory() + configure_directory = AiiDAConfigPathResolver.get_configuration_directory() print_status(ServiceStatus.UP, 'version', f'AiiDA v{__version__}') - print_status(ServiceStatus.UP, 'config', str(configure_directory)) + print_status(ServiceStatus.UP, 'config', configure_directory) manager = get_manager() diff --git a/src/aiida/cmdline/params/options/commands/setup.py b/src/aiida/cmdline/params/options/commands/setup.py index fc4f031aa3..d60f70a274 100644 --- a/src/aiida/cmdline/params/options/commands/setup.py +++ b/src/aiida/cmdline/params/options/commands/setup.py @@ -66,11 +66,12 @@ def get_repository_uri_default(ctx): """ import os - from aiida.manage.configuration.settings import get_configuration_directory + from aiida.manage.configuration.settings import AiiDAConfigPathResolver validate_profile_parameter(ctx) + configure_directory = AiiDAConfigPathResolver.get_configuration_directory() - return os.path.join(get_configuration_directory(), 'repository', ctx.params['profile'].name) + return os.path.join(configure_directory, 'repository', ctx.params['profile'].name) def get_quicksetup_repository_uri(ctx, param, value): diff --git a/src/aiida/storage/sqlite_dos/backend.py b/src/aiida/storage/sqlite_dos/backend.py index 52099f7391..ff24b17a6a 100644 --- a/src/aiida/storage/sqlite_dos/backend.py +++ b/src/aiida/storage/sqlite_dos/backend.py @@ -203,7 +203,9 @@ class Model(BaseModel, defer_build=True): filepath: str = Field( 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}'), + default_factory=lambda: str( + AiiDAConfigPathResolver.get_configuration_directory() / 'repository' / f'sqlite_dos_{uuid4().hex}' + ), ) @field_validator('filepath') diff --git a/tests/manage/configuration/test_config.py b/tests/manage/configuration/test_config.py index 88aae97a69..667ee0643b 100644 --- a/tests/manage/configuration/test_config.py +++ b/tests/manage/configuration/test_config.py @@ -16,10 +16,10 @@ import pytest from aiida.common import exceptions from aiida.manage.configuration import Profile, settings -from aiida.manage.configuration.settings import AiiDAConfigPathResolver 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.storage.sqlite_temp import SqliteTempBackend From aa6b44d3e2587bcb92b9a6c7b231c43e34db1f7a Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Wed, 20 Nov 2024 01:03:40 +0100 Subject: [PATCH 18/31] Explicitly load and set configuration folder in the main module level [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiida/__init__.py | 4 ++++ src/aiida/manage/configuration/settings.py | 5 ----- tests/manage/test_caching_config.py | 7 ++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/aiida/__init__.py b/src/aiida/__init__.py index 743c5009ab..4927ded769 100644 --- a/src/aiida/__init__.py +++ b/src/aiida/__init__.py @@ -21,6 +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 __copyright__ = ( 'Copyright (c), This file is part of the AiiDA platform. ' @@ -35,6 +36,9 @@ ) __paper_short__ = 'S. P. Huber et al., Scientific Data 7, 300 (2020).' +# Initialize the configuration directory settings +AiiDAConfigPathResolver.set_configuration_directory() + def get_strict_version(): """Return a distutils StrictVersion instance with the current distribution version diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 22be2be4ea..13d33fd03e 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -136,8 +136,3 @@ def _get_configuration_directory_from_envvar() -> pathlib.Path: break return dirpath_config or default_dirpath_config - - -# XXX: Can I move this to aiida.__init__??? -# Initialize the configuration directory settings -AiiDAConfigPathResolver.set_configuration_directory() diff --git a/tests/manage/test_caching_config.py b/tests/manage/test_caching_config.py index 5f3b5126eb..96461d9161 100644 --- a/tests/manage/test_caching_config.py +++ b/tests/manage/test_caching_config.py @@ -45,7 +45,8 @@ 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, settings + from aiida.manage.configuration import get_config_option, load_profile + from aiida.manage.configuration.settings import AiiDAConfigPathResolver # Store the current configuration instance and config directory path current_config = configuration.CONFIG @@ -57,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 - settings.set_configuration_directory(pathlib.Path(tmp_path)) + AiiDAConfigPathResolver.set_configuration_directory(pathlib.Path(tmp_path)) config_dictionary = json.loads( pathlib.Path(__file__) .parent.joinpath('configuration/migrations/test_samples/reference/6.json') @@ -86,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() - settings.set_configuration_directory(current_config_path) + AiiDAConfigPathResolver.set_configuration_directory(current_config_path) configuration.CONFIG = current_config load_profile(current_profile_name) From bdfedcaf985680dec1bce1dabd35bd635679f695 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 22 Nov 2024 01:36:08 +0100 Subject: [PATCH 19/31] rbsad --- src/aiida/manage/configuration/__init__.py | 2 +- src/aiida/manage/configuration/profile.py | 4 ++-- src/aiida/manage/configuration/settings.py | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index ad64a7bb67..eae8c65e08 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -67,7 +67,7 @@ def get_config_path(): - """Returns path to .aiida configuration directory.""" + """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) diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index d1d586f8b3..0534493b12 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -52,14 +52,14 @@ def __init__( self, name: str, config: abc.Mapping[str, Any], config_folder: pathlib.Path | None = None, validate: bool = True ): """Load a profile with the profile configuration.""" - _ = type_check(config, abc.Mapping) + type_check(config, abc.Mapping) if validate and not set(config.keys()).issuperset(self.REQUIRED_KEYS): raise exceptions.ConfigurationError( f'profile {name!r} configuration does not contain all required keys: {self.REQUIRED_KEYS}' ) self._name: str = name - self._attributes: dict[str, Any] = cast(dict[str, Any], deepcopy(config)) + self._attributes: dict[str, Any] = deepcopy(config) # type: ignore # Create a default UUID if not specified if self._attributes.get(self.KEY_UUID, None) is None: diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 13d33fd03e..2cb22e09d2 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -31,7 +31,9 @@ @final class AiiDAConfigPathResolver: - """Path resolver for getting daemon dir, daemon log dir and access control dir location.""" + """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.""" _glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME From a9980a2438f3b7ee6ac71566eea5955473f858e4 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 22 Nov 2024 01:45:52 +0100 Subject: [PATCH 20/31] 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) From 627d0da13e7bd93cabb1e5e84e74db30893f31c8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 00:46:25 +0000 Subject: [PATCH 21/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiida/manage/configuration/profile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index 0534493b12..64662c8e49 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -14,7 +14,7 @@ import pathlib from collections import abc from copy import deepcopy -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any from aiida.common import exceptions from aiida.common.lang import type_check @@ -59,7 +59,7 @@ def __init__( ) self._name: str = name - self._attributes: dict[str, Any] = deepcopy(config) # type: ignore + self._attributes: dict[str, Any] = deepcopy(config) # type: ignore # Create a default UUID if not specified if self._attributes.get(self.KEY_UUID, None) is None: From 6cef5559cd00d19923853f799bcddbc61aa86596 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 22 Nov 2024 02:36:20 +0100 Subject: [PATCH 22/31] rsb --- src/aiida/manage/configuration/settings.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 5877df0e7a..17e289088a 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -45,10 +45,14 @@ def set_configuration_directory(cls, aiida_config_folder: pathlib.Path | 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. + the path that is returned by ``get_configuration_directory_from_envvar``. + Or if the environment_variable not set, use the default. + 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() + _default_dirpath_config = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME + + aiida_config_folder = aiida_config_folder or _get_configuration_directory_from_envvar() + cls._glb_aiida_config_folder = aiida_config_folder or _default_dirpath_config _create_instance_directories(cls._glb_aiida_config_folder) @@ -113,7 +117,7 @@ def _create_instance_directories(aiida_config_folder: pathlib.Path | None) -> No _ = os.umask(umask) -def _get_configuration_directory_from_envvar() -> pathlib.Path: +def _get_configuration_directory_from_envvar() -> pathlib.Path | None: """Return the path of a config directory from the ``AIIDA_PATH`` environment variable. The environment variable should be a colon separated string of filepaths that either point directly to a config @@ -127,7 +131,7 @@ def _get_configuration_directory_from_envvar() -> pathlib.Path: default_dirpath_config = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME if environment_variable is None: - return default_dirpath_config + return None # Loop over all the paths in the ``AIIDA_PATH`` variable to see if any of them contain a configuration folder dirpath_config = None From b5cb0d742e350ce8aeb550961a023f6ccdaa1cfb Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 22 Nov 2024 10:18:07 +0100 Subject: [PATCH 23/31] rdh --- src/aiida/manage/configuration/__init__.py | 2 +- tests/conftest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index c56abae2c1..9907bb6a74 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -11,7 +11,6 @@ from __future__ import annotations -from aiida.manage.configuration.settings import AiiDAConfigDir # AUTO-GENERATED # fmt: off @@ -69,6 +68,7 @@ def get_config_path(): """Returns path to aiida configuration file.""" from .settings import DEFAULT_CONFIG_FILE_NAME + from aiida.manage.configuration.settings import AiiDAConfigDir return os.path.join(AiiDAConfigDir.get_configuration_directory(), DEFAULT_CONFIG_FILE_NAME) diff --git a/tests/conftest.py b/tests/conftest.py index 651c9eec25..0ba1ed3ee2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -30,7 +30,6 @@ 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 AiiDAConfigDir if t.TYPE_CHECKING: from aiida.manage.configuration.config import Config @@ -330,6 +329,7 @@ def empty_config(tmp_path) -> Config: """ from aiida.common.utils import Capturing from aiida.manage import configuration, get_manager + from aiida.manage.configuration.settings import AiiDAConfigDir manager = get_manager() From 6ec11a4f6abe6b89bbeda139057a471dfb9d37b9 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 22 Nov 2024 11:14:53 +0100 Subject: [PATCH 24/31] Move filepaths to config --- src/aiida/__init__.py | 3 --- src/aiida/engine/daemon/client.py | 18 ++++++------- src/aiida/manage/configuration/config.py | 31 ++++++++++++++++++++++ src/aiida/manage/configuration/profile.py | 19 ++++++------- src/aiida/manage/configuration/settings.py | 6 ++++- src/aiida/manage/profile_access.py | 3 ++- 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/aiida/__init__.py b/src/aiida/__init__.py index 42c5e8299b..69cdb38bed 100644 --- a/src/aiida/__init__.py +++ b/src/aiida/__init__.py @@ -36,9 +36,6 @@ ) __paper_short__ = 'S. P. Huber et al., Scientific Data 7, 300 (2020).' -# Initialize the configuration directory settings -AiiDAConfigDir.set_configuration_directory() - def get_strict_version(): """Return a distutils StrictVersion instance with the current distribution version diff --git a/src/aiida/engine/daemon/client.py b/src/aiida/engine/daemon/client.py index ef250802f7..4e47e7ed60 100644 --- a/src/aiida/engine/daemon/client.py +++ b/src/aiida/engine/daemon/client.py @@ -94,10 +94,10 @@ def __init__(self, profile: Profile): from aiida.common.docs import URL_NO_BROKER type_check(profile, Profile) - config = get_config() + self._config = get_config() self._profile = profile self._socket_directory: str | None = None - self._daemon_timeout: int = config.get_option('daemon.timeout', scope=profile.name) + self._daemon_timeout: int = self._config.get_option('daemon.timeout', scope=profile.name) if self._profile.process_control_backend is None: raise ConfigurationError( @@ -156,31 +156,31 @@ def virtualenv(self) -> str | None: @property def circus_log_file(self) -> str: - return self.profile.filepaths['circus']['log'] + return self._config.filepaths(self.profile)['circus']['log'] @property def circus_pid_file(self) -> str: - return self.profile.filepaths['circus']['pid'] + return self._config.filepaths(self.profile)['circus']['pid'] @property def circus_port_file(self) -> str: - return self.profile.filepaths['circus']['port'] + return self._config.filepaths(self.profile)['circus']['port'] @property def circus_socket_file(self) -> str: - return self.profile.filepaths['circus']['socket']['file'] + return self._config.filepaths(self.profile)['circus']['socket']['file'] @property def circus_socket_endpoints(self) -> dict[str, str]: - return self.profile.filepaths['circus']['socket'] + return self._config.filepaths(self.profile)['circus']['socket'] @property def daemon_log_file(self) -> str: - return self.profile.filepaths['daemon']['log'] + return self._config.filepaths(self.profile)['daemon']['log'] @property def daemon_pid_file(self) -> str: - return self.profile.filepaths['daemon']['pid'] + return self._config.filepaths(self.profile)['daemon']['pid'] def get_circus_port(self) -> int: """Retrieve the port for the circus controller, which should be written to the circus port file. diff --git a/src/aiida/manage/configuration/config.py b/src/aiida/manage/configuration/config.py index 4b1f032271..beb8b1a697 100644 --- a/src/aiida/manage/configuration/config.py +++ b/src/aiida/manage/configuration/config.py @@ -20,6 +20,7 @@ import io import json import os +from pathlib import Path import uuid from typing import Any, Dict, List, Optional, Tuple @@ -780,3 +781,33 @@ def _atomic_write(self, filepath=None): handle.flush() os.rename(handle.name, self.filepath) + + + def filepaths(self, profile: Profile): + """Return the filepaths used by this profile. + + :return: a dictionary of filepaths + """ + from aiida.manage.configuration.settings import AiiDAConfigPathResolver + + _config_path_resolver: AiiDAConfigPathResolver = AiiDAConfigPathResolver(Path(self.dirpath)) + daemon_dir = _config_path_resolver.daemon_dir + daemon_log_dir = _config_path_resolver.daemon_log_dir + + return { + 'circus': { + 'log': str(daemon_log_dir / f'circus-{profile.name}.log'), + 'pid': str(daemon_dir / f'circus-{profile.name}.pid'), + 'port': str(daemon_dir / f'circus-{profile.name}.port'), + 'socket': { + 'file': str(daemon_dir / f'circus-{profile.name}.sockets'), + 'controller': 'circus.c.sock', + 'pubsub': 'circus.p.sock', + 'stats': 'circus.s.sock', + }, + }, + 'daemon': { + 'log': str(daemon_log_dir / f'aiida-{profile.name}.log'), + 'pid': str(daemon_dir / f'aiida-{profile.name}.pid'), + }, + } diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index 64662c8e49..6e1f014d3e 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -18,7 +18,6 @@ from aiida.common import exceptions from aiida.common.lang import type_check -from aiida.manage.configuration.settings import AiiDAConfigPathResolver from .options import parse_option @@ -49,7 +48,7 @@ class Profile: ) def __init__( - self, name: str, config: abc.Mapping[str, Any], config_folder: pathlib.Path | None = None, validate: bool = True + self, name: str, config: abc.Mapping[str, Any], validate: bool = True ): """Load a profile with the profile configuration.""" type_check(config, abc.Mapping) @@ -67,8 +66,6 @@ def __init__( self._attributes[self.KEY_UUID] = uuid4().hex - self._config_path_resolver: AiiDAConfigPathResolver = AiiDAConfigPathResolver(config_folder) - def __repr__(self) -> str: return f'Profile' @@ -88,11 +85,6 @@ def uuid(self) -> str: def uuid(self, value: str) -> None: self._attributes[self.KEY_UUID] = value - @property - def config_path_resolver(self) -> AiiDAConfigPathResolver: - """The config_path_resolver property.""" - return self._config_path_resolver - @property def default_user_email(self) -> str | None: """Return the default user email.""" @@ -245,8 +237,13 @@ def filepaths(self): :return: a dictionary of filepaths """ - daemon_dir = self._config_path_resolver.daemon_dir - daemon_log_dir = self._config_path_resolver.daemon_log_dir + from aiida.common.warnings import warn_deprecation + from aiida.manage.configuration.settings import AiiDAConfigPathResolver + + warn_deprecation('This method has been deprecated', version=3) + + daemon_dir = AiiDAConfigPathResolver().daemon_dir + daemon_log_dir = AiiDAConfigPathResolver().daemon_log_dir return { 'circus': { diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 17e289088a..e942e06410 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -64,7 +64,7 @@ class AiiDAConfigPathResolver: The locations are all trivially derived from the config location, """ - def __init__(self, config_folder: pathlib.Path | None) -> None: + def __init__(self, config_folder: pathlib.Path | None = None) -> None: self._aiida_path = config_folder or AiiDAConfigDir.get_configuration_directory() @property @@ -148,3 +148,7 @@ def _get_configuration_directory_from_envvar() -> pathlib.Path | None: break return dirpath_config or default_dirpath_config + + +# Initialize the configuration directory settings +AiiDAConfigDir.set_configuration_directory() diff --git a/src/aiida/manage/profile_access.py b/src/aiida/manage/profile_access.py index 8602539e13..c65364af03 100644 --- a/src/aiida/manage/profile_access.py +++ b/src/aiida/manage/profile_access.py @@ -18,6 +18,7 @@ from aiida.common.exceptions import LockedProfileError, LockingProfileError from aiida.common.lang import type_check from aiida.manage.configuration import Profile +from aiida.manage.configuration.settings import AiiDAConfigPathResolver @typing.final @@ -49,7 +50,7 @@ def __init__(self, profile: Profile): _ = type_check(profile, Profile) self.profile = profile self.process = psutil.Process(os.getpid()) - self._dirpath_records = profile.config_path_resolver.access_control_dir / profile.name + self._dirpath_records = AiiDAConfigPathResolver().access_control_dir / profile.name self._dirpath_records.mkdir(exist_ok=True) def request_access(self) -> None: From 88fe04db83558174fb7d265ba5cc6a019cafd726 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:17:37 +0000 Subject: [PATCH 25/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiida/manage/configuration/__init__.py | 4 ++-- src/aiida/manage/configuration/config.py | 3 +-- src/aiida/manage/configuration/profile.py | 4 +--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index 9907bb6a74..94e26ed30f 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -11,7 +11,6 @@ from __future__ import annotations - # AUTO-GENERATED # fmt: off from .migrations import * @@ -67,9 +66,10 @@ def get_config_path(): """Returns path to aiida configuration file.""" - from .settings import DEFAULT_CONFIG_FILE_NAME from aiida.manage.configuration.settings import AiiDAConfigDir + from .settings import DEFAULT_CONFIG_FILE_NAME + return os.path.join(AiiDAConfigDir.get_configuration_directory(), DEFAULT_CONFIG_FILE_NAME) diff --git a/src/aiida/manage/configuration/config.py b/src/aiida/manage/configuration/config.py index beb8b1a697..7c03c5c462 100644 --- a/src/aiida/manage/configuration/config.py +++ b/src/aiida/manage/configuration/config.py @@ -20,8 +20,8 @@ import io import json import os -from pathlib import Path import uuid +from pathlib import Path from typing import Any, Dict, List, Optional, Tuple from pydantic import ( @@ -782,7 +782,6 @@ def _atomic_write(self, filepath=None): handle.flush() os.rename(handle.name, self.filepath) - def filepaths(self, profile: Profile): """Return the filepaths used by this profile. diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index 6e1f014d3e..f95c7c6b28 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -47,9 +47,7 @@ class Profile: KEY_PROCESS, ) - def __init__( - self, name: str, config: abc.Mapping[str, Any], validate: bool = True - ): + def __init__(self, name: str, config: abc.Mapping[str, Any], validate: bool = True): """Load a profile with the profile configuration.""" type_check(config, abc.Mapping) if validate and not set(config.keys()).issuperset(self.REQUIRED_KEYS): From 0d87b9b867199476a4981729a9358b8ea637e505 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Fri, 22 Nov 2024 14:49:26 +0100 Subject: [PATCH 26/31] f-pre-commit --- src/aiida/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/aiida/__init__.py b/src/aiida/__init__.py index 69cdb38bed..743c5009ab 100644 --- a/src/aiida/__init__.py +++ b/src/aiida/__init__.py @@ -21,7 +21,6 @@ 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 AiiDAConfigDir __copyright__ = ( 'Copyright (c), This file is part of the AiiDA platform. ' From 9fd3dae31160a5c2b439b288e18f70d7413ad5b6 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Tue, 26 Nov 2024 15:08:59 +0100 Subject: [PATCH 27/31] Rename the set/get method of AiiDAConfigDir to just set and get --- src/aiida/cmdline/commands/cmd_presto.py | 2 +- src/aiida/cmdline/commands/cmd_profile.py | 2 +- src/aiida/cmdline/commands/cmd_status.py | 2 +- .../cmdline/params/options/commands/setup.py | 2 +- src/aiida/manage/configuration/__init__.py | 2 +- src/aiida/manage/configuration/settings.py | 10 ++++----- src/aiida/manage/tests/pytest_fixtures.py | 6 ++--- src/aiida/storage/sqlite_dos/backend.py | 2 +- .../tools/pytest_fixtures/configuration.py | 4 ++-- tests/conftest.py | 4 ++-- tests/manage/configuration/test_config.py | 22 +++++++++---------- tests/manage/test_caching_config.py | 4 ++-- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/aiida/cmdline/commands/cmd_presto.py b/src/aiida/cmdline/commands/cmd_presto.py index 7c396e602d..eeb98fad75 100644 --- a/src/aiida/cmdline/commands/cmd_presto.py +++ b/src/aiida/cmdline/commands/cmd_presto.py @@ -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 = AiiDAConfigDir.get_configuration_directory() + aiida_config_folder = AiiDAConfigDir.get() return { 'database_hostname': postgres_hostname, diff --git a/src/aiida/cmdline/commands/cmd_profile.py b/src/aiida/cmdline/commands/cmd_profile.py index 35f00b7bdd..7cb0e018ae 100644 --- a/src/aiida/cmdline/commands/cmd_profile.py +++ b/src/aiida/cmdline/commands/cmd_profile.py @@ -171,7 +171,7 @@ def profile_list(): # they can at least verify that it is correctly set. from aiida.manage.configuration.settings import AiiDAConfigDir - echo.echo_report(f'configuration folder: {AiiDAConfigDir.get_configuration_directory()}') + echo.echo_report(f'configuration folder: {AiiDAConfigDir.get()}') 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 72d9698ddf..85ef292fa7 100644 --- a/src/aiida/cmdline/commands/cmd_status.py +++ b/src/aiida/cmdline/commands/cmd_status.py @@ -65,7 +65,7 @@ def verdi_status(print_traceback, no_rmq): from aiida.manage.manager import get_manager exit_code = ExitCode.SUCCESS - configure_directory = AiiDAConfigDir.get_configuration_directory() + configure_directory = AiiDAConfigDir.get() 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 0845ac34bf..49cfc1e121 100644 --- a/src/aiida/cmdline/params/options/commands/setup.py +++ b/src/aiida/cmdline/params/options/commands/setup.py @@ -69,7 +69,7 @@ def get_repository_uri_default(ctx): from aiida.manage.configuration.settings import AiiDAConfigDir validate_profile_parameter(ctx) - configure_directory = AiiDAConfigDir.get_configuration_directory() + configure_directory = AiiDAConfigDir.get() 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 94e26ed30f..8779333045 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -70,7 +70,7 @@ def get_config_path(): from .settings import DEFAULT_CONFIG_FILE_NAME - return os.path.join(AiiDAConfigDir.get_configuration_directory(), DEFAULT_CONFIG_FILE_NAME) + return os.path.join(AiiDAConfigDir.get(), 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 e942e06410..05ae00d061 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -36,16 +36,16 @@ class AiiDAConfigDir: _glb_aiida_config_folder: pathlib.Path = pathlib.Path(DEFAULT_AIIDA_PATH).expanduser() / DEFAULT_CONFIG_DIR_NAME @classmethod - def get_configuration_directory(cls): + def get(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: + def set(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``. + the path that is returned by ``_get_configuration_directory_from_envvar``. Or if the environment_variable not set, use the default. If the directory does not exist yet, it is created, together with all its subdirectories. """ @@ -65,7 +65,7 @@ class AiiDAConfigPathResolver: """ def __init__(self, config_folder: pathlib.Path | None = None) -> None: - self._aiida_path = config_folder or AiiDAConfigDir.get_configuration_directory() + self._aiida_path = config_folder or AiiDAConfigDir.get() @property def aiida_path(self) -> pathlib.Path: @@ -151,4 +151,4 @@ def _get_configuration_directory_from_envvar() -> pathlib.Path | None: # Initialize the configuration directory settings -AiiDAConfigDir.set_configuration_directory() +AiiDAConfigDir.set() diff --git a/src/aiida/manage/tests/pytest_fixtures.py b/src/aiida/manage/tests/pytest_fixtures.py index 09818429cd..40434532e9 100644 --- a/src/aiida/manage/tests/pytest_fixtures.py +++ b/src/aiida/manage/tests/pytest_fixtures.py @@ -161,7 +161,7 @@ def aiida_instance( :return: The configuration the AiiDA instance loaded for this test session. """ from aiida.manage import configuration - from aiida.manage.configuration import settings + from aiida.manage.configuration.settings import AiiDAConfigDir if aiida_test_profile: yield configuration.get_config() @@ -178,7 +178,7 @@ def aiida_instance( dirpath_config = tmp_path_factory.mktemp('config') os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = str(dirpath_config) - settings.set_configuration_directory(dirpath_config) + AiiDAConfigDir.set(dirpath_config) configuration.CONFIG = configuration.load_config(create=True) try: @@ -190,7 +190,7 @@ def aiida_instance( else: os.environ[settings.DEFAULT_AIIDA_PATH_VARIABLE] = current_path_variable - settings.set_configuration_directory(current_config_path) + AiiDAConfigDir.set(current_config_path) configuration.CONFIG = current_config if current_profile: aiida_manager.load_profile(current_profile.name, allow_switch=True) diff --git a/src/aiida/storage/sqlite_dos/backend.py b/src/aiida/storage/sqlite_dos/backend.py index cf1625db01..cc4b306ae4 100644 --- a/src/aiida/storage/sqlite_dos/backend.py +++ b/src/aiida/storage/sqlite_dos/backend.py @@ -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( - AiiDAConfigDir.get_configuration_directory() / 'repository' / f'sqlite_dos_{uuid4().hex}' + AiiDAConfigDir.get() / '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 7efd22265e..ed06072b71 100644 --- a/src/aiida/tools/pytest_fixtures/configuration.py +++ b/src/aiida/tools/pytest_fixtures/configuration.py @@ -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) - AiiDAConfigDir.set_configuration_directory(dirpath_config) + AiiDAConfigDir.set(dirpath_config) config = get_config(create=True) try: @@ -63,7 +63,7 @@ def factory(dirpath: pathlib.Path): finally: if current_config: reset_config() - AiiDAConfigDir.set_configuration_directory(pathlib.Path(current_config.dirpath)) + AiiDAConfigDir.set(pathlib.Path(current_config.dirpath)) get_config() if current_path_variable is None: diff --git a/tests/conftest.py b/tests/conftest.py index 7c136f4878..b50cf90843 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -387,7 +387,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`. - AiiDAConfigDir.set_configuration_directory(tmp_path) + AiiDAConfigDir.set(tmp_path) # The constructor of `Config` called by `load_config` will print warning messages about migrating it with Capturing(): @@ -405,7 +405,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. - AiiDAConfigDir.set_configuration_directory(pathlib.Path(current_config_path)) + AiiDAConfigDir.set(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 2af16e3ec8..7fedb4bf2a 100644 --- a/tests/manage/configuration/test_config.py +++ b/tests/manage/configuration/test_config.py @@ -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. - AiiDAConfigDir.set_configuration_directory() + AiiDAConfigDir.set() @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 - AiiDAConfigDir.set_configuration_directory() + AiiDAConfigDir.set() config_folder = chdir_tmp_path / settings.DEFAULT_CONFIG_DIR_NAME assert os.path.isdir(config_folder) - assert AiiDAConfigDir.get_configuration_directory() == pathlib.Path(config_folder) + assert AiiDAConfigDir.get() == 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) - AiiDAConfigDir.set_configuration_directory() + AiiDAConfigDir.set() # 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 AiiDAConfigDir.get_configuration_directory() == config_folder + assert AiiDAConfigDir.get() == 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) - AiiDAConfigDir.set_configuration_directory() + AiiDAConfigDir.set() # 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 AiiDAConfigDir.get_configuration_directory() == config_folder + assert AiiDAConfigDir.get() == 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) - AiiDAConfigDir.set_configuration_directory() + AiiDAConfigDir.set() # 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 AiiDAConfigDir.get_configuration_directory() == config_folder + assert AiiDAConfigDir.get() == 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 - AiiDAConfigDir.set_configuration_directory() + AiiDAConfigDir.set() # 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 AiiDAConfigDir.get_configuration_directory() == config_folder + assert AiiDAConfigDir.get() == 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 681ae90350..932a32873c 100644 --- a/tests/manage/test_caching_config.py +++ b/tests/manage/test_caching_config.py @@ -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 - AiiDAConfigDir.set_configuration_directory(pathlib.Path(tmp_path)) + AiiDAConfigDir.set(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() - AiiDAConfigDir.set_configuration_directory(current_config_path) + AiiDAConfigDir.set(current_config_path) configuration.CONFIG = current_config load_profile(current_profile_name) From 35b7ae2590f671934f04c0de723601880bcfdc97 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:09:05 +0000 Subject: [PATCH 28/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiida/storage/sqlite_dos/backend.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/aiida/storage/sqlite_dos/backend.py b/src/aiida/storage/sqlite_dos/backend.py index cc4b306ae4..2443fee259 100644 --- a/src/aiida/storage/sqlite_dos/backend.py +++ b/src/aiida/storage/sqlite_dos/backend.py @@ -203,9 +203,7 @@ class Model(BaseModel, defer_build=True): filepath: str = Field( title='Directory of the backend', description='Filepath of the directory in which to store data for this backend.', - default_factory=lambda: str( - AiiDAConfigDir.get() / 'repository' / f'sqlite_dos_{uuid4().hex}' - ), + default_factory=lambda: str(AiiDAConfigDir.get() / 'repository' / f'sqlite_dos_{uuid4().hex}'), ) @field_validator('filepath') From 341bab57c2e52ba275750507ed47d8a68eec3a7b Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Tue, 26 Nov 2024 15:17:23 +0100 Subject: [PATCH 29/31] Revert changes for type checkings --- src/aiida/manage/configuration/profile.py | 52 +++++++++++------------ src/aiida/manage/tests/pytest_fixtures.py | 1 + 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index f95c7c6b28..eae522a97b 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -10,14 +10,13 @@ from __future__ import annotations +import collections import os import pathlib -from collections import abc from copy import deepcopy -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Type from aiida.common import exceptions -from aiida.common.lang import type_check from .options import parse_option @@ -30,33 +29,34 @@ class Profile: """Class that models a profile as it is stored in the configuration file of an AiiDA instance.""" - KEY_UUID: str = 'PROFILE_UUID' - KEY_DEFAULT_USER_EMAIL: str = 'default_user_email' - KEY_STORAGE: str = 'storage' - KEY_PROCESS: str = 'process_control' - KEY_STORAGE_BACKEND: str = 'backend' - KEY_STORAGE_CONFIG: str = 'config' - KEY_PROCESS_BACKEND: str = 'backend' - KEY_PROCESS_CONFIG: str = 'config' - KEY_OPTIONS: str = 'options' - KEY_TEST_PROFILE: str = 'test_profile' + KEY_UUID = 'PROFILE_UUID' + KEY_DEFAULT_USER_EMAIL = 'default_user_email' + KEY_STORAGE = 'storage' + KEY_PROCESS = 'process_control' + KEY_STORAGE_BACKEND = 'backend' + KEY_STORAGE_CONFIG = 'config' + KEY_PROCESS_BACKEND = 'backend' + KEY_PROCESS_CONFIG = 'config' + KEY_OPTIONS = 'options' + KEY_TEST_PROFILE = 'test_profile' # keys that are expected to be in the parsed configuration - REQUIRED_KEYS: tuple[str, str] = ( + REQUIRED_KEYS = ( KEY_STORAGE, KEY_PROCESS, ) - def __init__(self, name: str, config: abc.Mapping[str, Any], validate: bool = True): + def __init__(self, name: str, config: Mapping[str, Any], validate=True): """Load a profile with the profile configuration.""" - type_check(config, abc.Mapping) + if not isinstance(config, collections.abc.Mapping): + raise TypeError(f'config should be a mapping but is {type(config)}') if validate and not set(config.keys()).issuperset(self.REQUIRED_KEYS): raise exceptions.ConfigurationError( f'profile {name!r} configuration does not contain all required keys: {self.REQUIRED_KEYS}' ) - self._name: str = name - self._attributes: dict[str, Any] = deepcopy(config) # type: ignore + self._name = name + self._attributes: Dict[str, Any] = deepcopy(config) # type: ignore[arg-type] # Create a default UUID if not specified if self._attributes.get(self.KEY_UUID, None) is None: @@ -84,12 +84,12 @@ def uuid(self, value: str) -> None: self._attributes[self.KEY_UUID] = value @property - def default_user_email(self) -> str | None: + def default_user_email(self) -> Optional[str]: """Return the default user email.""" return self._attributes.get(self.KEY_DEFAULT_USER_EMAIL, None) @default_user_email.setter - def default_user_email(self, value: str | None) -> None: + def default_user_email(self, value: Optional[str]) -> None: """Set the default user email.""" self._attributes[self.KEY_DEFAULT_USER_EMAIL] = value @@ -99,11 +99,11 @@ def storage_backend(self) -> str: return self._attributes[self.KEY_STORAGE][self.KEY_STORAGE_BACKEND] @property - def storage_config(self) -> dict[str, Any]: + def storage_config(self) -> Dict[str, Any]: """Return the configuration required by the storage backend.""" return self._attributes[self.KEY_STORAGE][self.KEY_STORAGE_CONFIG] - def set_storage(self, name: str, config: dict[str, Any]) -> None: + def set_storage(self, name: str, config: Dict[str, Any]) -> None: """Set the storage backend and its configuration. :param name: the name of the storage backend @@ -114,7 +114,7 @@ def set_storage(self, name: str, config: dict[str, Any]) -> None: self._attributes[self.KEY_STORAGE][self.KEY_STORAGE_CONFIG] = config @property - def storage_cls(self) -> type['StorageBackend']: + def storage_cls(self) -> Type['StorageBackend']: """Return the storage backend class for this profile.""" from aiida.plugins import StorageFactory @@ -126,11 +126,11 @@ def process_control_backend(self) -> str | None: return self._attributes[self.KEY_PROCESS][self.KEY_PROCESS_BACKEND] @property - def process_control_config(self) -> dict[str, Any]: + def process_control_config(self) -> Dict[str, Any]: """Return the configuration required by the process control backend.""" return self._attributes[self.KEY_PROCESS][self.KEY_PROCESS_CONFIG] or {} - def set_process_controller(self, name: str, config: dict[str, Any]) -> None: + def set_process_controller(self, name: str, config: Dict[str, Any]) -> None: """Set the process control backend and its configuration. :param name: the name of the process backend @@ -175,7 +175,7 @@ def name(self): return self._name @property - def dictionary(self) -> dict[str, Any]: + def dictionary(self) -> Dict[str, Any]: """Return the profile attributes as a dictionary with keys as it is stored in the config :return: the profile configuration dictionary diff --git a/src/aiida/manage/tests/pytest_fixtures.py b/src/aiida/manage/tests/pytest_fixtures.py index 40434532e9..6c5d04d3bc 100644 --- a/src/aiida/manage/tests/pytest_fixtures.py +++ b/src/aiida/manage/tests/pytest_fixtures.py @@ -161,6 +161,7 @@ def aiida_instance( :return: The configuration the AiiDA instance loaded for this test session. """ from aiida.manage import configuration + from aiida.manage.configuration import settings from aiida.manage.configuration.settings import AiiDAConfigDir if aiida_test_profile: From b540813e68266447ee98fa2e504c270698d75b66 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Wed, 27 Nov 2024 09:32:19 +0100 Subject: [PATCH 30/31] rseb --- src/aiida/manage/configuration/profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aiida/manage/configuration/profile.py b/src/aiida/manage/configuration/profile.py index eae522a97b..93a3c5c911 100644 --- a/src/aiida/manage/configuration/profile.py +++ b/src/aiida/manage/configuration/profile.py @@ -238,7 +238,7 @@ def filepaths(self): from aiida.common.warnings import warn_deprecation from aiida.manage.configuration.settings import AiiDAConfigPathResolver - warn_deprecation('This method has been deprecated', version=3) + warn_deprecation('This method has been deprecated, use `filepaths` method from `Config` obj instead', version=3) daemon_dir = AiiDAConfigPathResolver().daemon_dir daemon_log_dir = AiiDAConfigPathResolver().daemon_log_dir From 6e26eaf147eb7ecad26ec3400debf37b2b562a3f Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Wed, 27 Nov 2024 18:14:34 +0100 Subject: [PATCH 31/31] rda --- src/aiida/manage/configuration/__init__.py | 4 +--- src/aiida/manage/configuration/config.py | 2 +- src/aiida/manage/configuration/settings.py | 11 +++++------ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/aiida/manage/configuration/__init__.py b/src/aiida/manage/configuration/__init__.py index 8779333045..097fcfc012 100644 --- a/src/aiida/manage/configuration/__init__.py +++ b/src/aiida/manage/configuration/__init__.py @@ -66,9 +66,7 @@ def get_config_path(): """Returns path to aiida configuration file.""" - from aiida.manage.configuration.settings import AiiDAConfigDir - - from .settings import DEFAULT_CONFIG_FILE_NAME + from .settings import DEFAULT_CONFIG_FILE_NAME, AiiDAConfigDir return os.path.join(AiiDAConfigDir.get(), DEFAULT_CONFIG_FILE_NAME) diff --git a/src/aiida/manage/configuration/config.py b/src/aiida/manage/configuration/config.py index 7c03c5c462..fff83f4330 100644 --- a/src/aiida/manage/configuration/config.py +++ b/src/aiida/manage/configuration/config.py @@ -783,7 +783,7 @@ def _atomic_write(self, filepath=None): os.rename(handle.name, self.filepath) def filepaths(self, profile: Profile): - """Return the filepaths used by this profile. + """Return the filepaths used by a profile. :return: a dictionary of filepaths """ diff --git a/src/aiida/manage/configuration/settings.py b/src/aiida/manage/configuration/settings.py index 05ae00d061..f47a2dd66e 100644 --- a/src/aiida/manage/configuration/settings.py +++ b/src/aiida/manage/configuration/settings.py @@ -59,9 +59,8 @@ def set(cls, aiida_config_folder: pathlib.Path | None = None) -> None: @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, + """For resolving configuration directory, daemon dir, daemon log dir and access control dir. + The locations are all trivially derived from the config directory. """ def __init__(self, config_folder: pathlib.Path | None = None) -> None: @@ -87,9 +86,9 @@ def access_control_dir(self) -> pathlib.Path: def _create_instance_directories(aiida_config_folder: pathlib.Path | None) -> None: """Create the base directories required for a new AiiDA instance. - This will create the base AiiDA directory defined by the ``aiida_config_folder`` if not provided - ``_glb_aiida_config_folder`` will be the default config folder, unless it already exists. - Subsequently, it will create the daemon directory within it and the daemon log directory. + This will create the base AiiDA directory in ``aiida_config_folder``. + If it not provided, the directory returned from ``AiiDAConfigDir.get()`` will be the default config folder, + unless it already exists. Subsequently, it will create the daemon directory within it and the daemon log directory. """ from aiida.common import ConfigurationError