Skip to content

Commit

Permalink
hehe, this looks like real Singleton
Browse files Browse the repository at this point in the history
  • Loading branch information
unkcpz committed Nov 19, 2024
1 parent dd7f04f commit 2ce9080
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 53 deletions.
6 changes: 4 additions & 2 deletions src/aiida/manage/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

from __future__ import annotations

from aiida.manage.configuration.settings import AiiDAConfigPathResolver

# AUTO-GENERATED
# fmt: off
from .migrations import *
Expand Down Expand Up @@ -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':
Expand Down
6 changes: 2 additions & 4 deletions src/aiida/manage/configuration/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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<uuid={self.uuid!r} name={self.name!r}>'
Expand Down
53 changes: 27 additions & 26 deletions src/aiida/manage/configuration/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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()
4 changes: 2 additions & 2 deletions src/aiida/storage/sqlite_dos/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand Down
6 changes: 4 additions & 2 deletions src/aiida/tools/pytest_fixtures/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import pytest

from aiida.manage.configuration.settings import AiiDAConfigPathResolver

if t.TYPE_CHECKING:
from aiida.manage.configuration.config import Config

Expand Down Expand Up @@ -53,15 +55,15 @@ 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:
yield config
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:
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand All @@ -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():
Expand All @@ -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)
Expand Down
27 changes: 13 additions & 14 deletions tests/manage/configuration/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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')
Expand All @@ -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):
Expand All @@ -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:
Expand Down

0 comments on commit 2ce9080

Please sign in to comment.