Skip to content

Commit

Permalink
Merge branch 'main' into lazy-config
Browse files Browse the repository at this point in the history
  • Loading branch information
danielhollas committed Oct 10, 2023
2 parents 4d6f88d + 5ca609b commit fa00ee6
Show file tree
Hide file tree
Showing 21 changed files with 213 additions and 164 deletions.
16 changes: 0 additions & 16 deletions .devcontainer/Dockerfile

This file was deleted.

15 changes: 11 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
{
"dockerComposeFile": "docker-compose.yml",
"service": "aiida",
"workspaceFolder": "/home/aiida/aiida-core",
"postCreateCommand": "bash ./.devcontainer/post_create.sh",
"waitFor": "postCreateCommand",
"service": "daemon",
"workspaceFolder": "/workspaces/aiida-core",
"postCreateCommand": "/etc/init/aiida-prepare.sh",
"postStartCommand": "pip install -e /workspaces/aiida-core[tests,docs,rest,atomic_tools,pre-commit]",
"postAttachCommand": "verdi daemon start",
"waitFor": "postStartCommand",
"containerUser": "aiida",
"remoteUser": "aiida",
"remoteEnv": {
"HOME": "/home/aiida"
},
"customizations": {
"vscode": {
"extensions": ["ms-python.python", "eamodio.gitlens"]
Expand Down
80 changes: 32 additions & 48 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,54 +1,38 @@
---
version: '3.4'

services:

rabbitmq:
image: rabbitmq:3.8.3-management
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
ports:
- '5672:5672'
- '15672:15672'
database:
image: postgres:15
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_HOST_AUTH_METHOD: trust
healthcheck:
test: [ "CMD-SHELL", "pg_isready"]
interval: 5s
timeout: 5s
retries: 10

healthcheck:
test: rabbitmq-diagnostics -q ping
interval: 30s
timeout: 30s
retries: 5
networks:
- aiida
messaging:
image: rabbitmq:3.8.14-management
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
healthcheck:
test: rabbitmq-diagnostics check_port_connectivity
interval: 30s
timeout: 30s
retries: 10

postgres:
image: postgres:12
ports:
- '5432:5432'
networks:
- aiida
environment:
POSTGRES_HOST_AUTH_METHOD: trust

aiida:
#image: "aiidateam/aiida-core:main"
image: "aiida-core-dev"
build:
# need to add the parent directory to context to copy over new configure-aiida.sh
context: ..
dockerfile: .devcontainer/Dockerfile
user: aiida
environment:
DB_HOST: postgres
BROKER_HOST: rabbitmq

# no need for /sbin/my_init
entrypoint: tail -f /dev/null
volumes:
- ..:/home/aiida/aiida-core:cached
networks:
- aiida
depends_on:
- rabbitmq
- postgres

networks:
aiida:
daemon:
image: ghcr.io/aiidateam/aiida-core-base:edge
user: aiida
entrypoint: tail -f /dev/null
environment:
SETUP_DEFAULT_AIIDA_PROFILE: 'true'
TZ: 'Europe/Zurich'
depends_on:
database:
condition: service_healthy
4 changes: 0 additions & 4 deletions .devcontainer/post_create.sh

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ AiiDA (www.aiida.net) is a workflow manager for computational science with a str
|Getting help| [![Docs status](https://readthedocs.org/projects/aiida-core/badge)](http://aiida-core.readthedocs.io/) [![Discourse status](https://img.shields.io/discourse/status?server=https%3A%2F%2Faiida.discourse.group%2F)](https://aiida.discourse.group/)
|Build status| [![Build Status](https://github.com/aiidateam/aiida-core/actions/workflows/ci-code.yml/badge.svg)](https://github.com/aiidateam/aiida-core/actions) [![Coverage Status](https://codecov.io/gh/aiidateam/aiida-core/branch/main/graph/badge.svg)](https://codecov.io/gh/aiidateam/aiida-core) [Benchmarks](https://aiidateam.github.io/aiida-core/dev/bench/ubuntu-22.04/psql_dos/) |
|Activity| [![PyPI-downloads](https://img.shields.io/pypi/dm/aiida-core.svg?style=flat)](https://pypistats.org/packages/aiida-core) [![Commit Activity](https://img.shields.io/github/commit-activity/m/aiidateam/aiida-core.svg)](https://github.com/aiidateam/aiida-core/pulse)
|Community| [![Affiliated with NumFOCUS](https://img.shields.io/badge/NumFOCUS-affiliated%20project-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org/sponsored-projects/affiliated-projects) [![Twitter](https://img.shields.io/twitter/follow/aiidateam.svg?style=social&label=Follow)](https://twitter.com/aiidateam)
|Community| [![Discourse](https://img.shields.io/discourse/topics?server=https%3A%2F%2Faiida.discourse.group%2F&logo=discourse)](https://aiida.discourse.group/) [![Affiliated with NumFOCUS](https://img.shields.io/badge/NumFOCUS-affiliated%20project-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org/sponsored-projects/affiliated-projects) [![Twitter](https://img.shields.io/twitter/follow/aiidateam.svg?style=social&label=Follow)](https://twitter.com/aiidateam)


## Features
Expand Down
4 changes: 2 additions & 2 deletions aiida/manage/configuration/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
###########################################################################
"""Module that defines the configuration file of an AiiDA instance and functions to create and load it."""
import codecs
from functools import lru_cache
from functools import cache
from importlib.resources import files
import json
import os
Expand All @@ -28,7 +28,7 @@
SCHEMA_FILE = 'config-v9.schema.json'


@lru_cache(1)
@cache
def config_schema() -> Dict[str, Any]:
"""Return the configuration schema."""
return json.loads(files(schema_module).joinpath(SCHEMA_FILE).read_text(encoding='utf8'))
Expand Down
51 changes: 24 additions & 27 deletions aiida/manage/tests/pytest_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import asyncio
import contextlib
import copy
import inspect
import io
import os
Expand All @@ -33,6 +32,7 @@
import uuid
import warnings

from importlib_metadata import EntryPoints
import plumpy
import pytest
import wrapt
Expand Down Expand Up @@ -759,9 +759,16 @@ def suppress_deprecations(wrapped, _, args, kwargs):
class EntryPointManager:
"""Manager to temporarily add or remove entry points."""

@staticmethod
def eps():
return plugins.entry_point.eps()
def __init__(self, entry_points: EntryPoints):
self.entry_points = entry_points

def eps(self) -> EntryPoints:
return self.entry_points

def eps_select(self, group, name=None) -> EntryPoints:
if name is None:
return self.eps().select(group=group)
return self.eps().select(group=group, name=name)

@staticmethod
def _validate_entry_point(entry_point_string: str | None, group: str | None, name: str | None) -> tuple[str, str]:
Expand Down Expand Up @@ -791,7 +798,6 @@ def _validate_entry_point(entry_point_string: str | None, group: str | None, nam

return group, name

@suppress_deprecations
def add(
self,
value: type | str,
Expand All @@ -817,9 +823,8 @@ def add(

group, name = self._validate_entry_point(entry_point_string, group, name)
entry_point = plugins.entry_point.EntryPoint(name, value, group)
self.eps()[group].append(entry_point)
self.entry_points = EntryPoints(self.entry_points + (entry_point,))

@suppress_deprecations
def remove(
self, entry_point_string: str | None = None, *, name: str | None = None, group: str | None = None
) -> None:
Expand All @@ -835,31 +840,23 @@ def remove(
:raises ValueError: If `entry_point_string` is not a complete entry point string with group and name.
"""
group, name = self._validate_entry_point(entry_point_string, group, name)

for entry_point in self.eps()[group]:
if entry_point.name == name:
self.eps()[group].remove(entry_point)
break
else:
try:
self.entry_points[name]
except KeyError:
raise KeyError(f'entry point `{name}` does not exist in group `{group}`.')
self.entry_points = EntryPoints((ep for ep in self.entry_points if not (ep.name == name and ep.group == group)))


@pytest.fixture
def entry_points(monkeypatch) -> EntryPointManager:
"""Return an instance of the ``EntryPointManager`` which allows to temporarily add or remove entry points.
This fixture creates a deep copy of the entry point cache returned by the :func:`aiida.plugins.entry_point.eps`
method and then monkey patches that function to return the deepcopy. This ensures that the changes on the entry
point cache performed during the test through the manager are undone at the end of the function scope.
.. note:: This fixture does not use the ``suppress_deprecations`` decorator on purpose, but instead adds it manually
inside the fixture's body. The reason is that otherwise all deprecations would be suppressed for the entire
scope of the fixture, including those raised by the code run in the test using the fixture, which is not
desirable.
This fixture monkey patches the entry point caches returned by
the :func:`aiida.plugins.entry_point.eps` and :func:`aiida.plugins.entry_point.eps_select` functions
to class methods of the ``EntryPointManager`` so that we can dynamically add / remove entry points.
Note that we do not need a deepcopy here as ``eps()`` returns an immutable ``EntryPoints`` tuple type.
"""
with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=DeprecationWarning)
eps_copy = copy.deepcopy(plugins.entry_point.eps())
monkeypatch.setattr(plugins.entry_point, 'eps', lambda: eps_copy)
yield EntryPointManager()
epm = EntryPointManager(plugins.entry_point.eps())
monkeypatch.setattr(plugins.entry_point, 'eps', epm.eps)
monkeypatch.setattr(plugins.entry_point, 'eps_select', epm.eps_select)
yield epm
2 changes: 1 addition & 1 deletion aiida/orm/autogroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def validate(strings: list[str] | None):
"""Validate the list of strings passed to set_include and set_exclude."""
if strings is None:
return
valid_prefixes = set(['aiida.node', 'aiida.calculations', 'aiida.workflows', 'aiida.data'])
valid_prefixes = {'aiida.node', 'aiida.calculations', 'aiida.workflows', 'aiida.data'}
for string in strings:
pieces = string.split(':')
if len(pieces) != 2:
Expand Down
48 changes: 32 additions & 16 deletions aiida/plugins/entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
# For further information please visit http://www.aiida.net #
###########################################################################
"""Module to manage loading entrypoints."""
from __future__ import annotations

import enum
import functools
import traceback
Expand All @@ -30,9 +32,30 @@
ENTRY_POINT_STRING_SEPARATOR = ':'


@functools.lru_cache(maxsize=1)
def eps():
return _eps()
@functools.cache
def eps() -> EntryPoints:
"""Cache around entry_points()
This call takes around 50ms!
NOTE: For faster lookups, we sort the ``EntryPoints`` alphabetically
by the group name so that 'aiida.' groups come up first.
Unfortunately, this does not help with the entry_points.select() filter,
which will always iterate over all entry points since it looks for
possible duplicate entries.
"""
entry_points = _eps()
return EntryPoints(sorted(entry_points, key=lambda x: x.group))


@functools.lru_cache(maxsize=100)
def eps_select(group: str, name: str | None = None) -> EntryPoints:
"""
A thin wrapper around entry_points.select() calls, which are
expensive so we want to cache them.
"""
if name is None:
return eps().select(group=group)
return eps().select(group=group, name=name)


class EntryPointFormat(enum.Enum):
Expand Down Expand Up @@ -254,8 +277,7 @@ def get_entry_point_groups() -> Set[str]:

def get_entry_point_names(group: str, sort: bool = True) -> List[str]:
"""Return the entry points within a group."""
all_eps = eps()
group_names = list(all_eps.select(group=group).names)
group_names = list(get_entry_points(group).names)
if sort:
return sorted(group_names)
return group_names
Expand All @@ -268,7 +290,7 @@ def get_entry_points(group: str) -> EntryPoints:
:param group: the entry point group
:return: a list of entry points
"""
return eps().select(group=group)
return eps_select(group=group)


def get_entry_point(group: str, name: str) -> EntryPoint:
Expand All @@ -283,7 +305,7 @@ def get_entry_point(group: str, name: str) -> EntryPoint:
"""
# The next line should be removed for ``aiida-core==3.0`` when the old deprecated entry points are fully removed.
name = convert_potentially_deprecated_entry_point(group, name)
found = eps().select(group=group, name=name)
found = eps_select(group=group, name=name)
if name not in found.names:
raise MissingEntryPointError(f"Entry point '{name}' not found in group '{group}'")
# If multiple entry points are found and they have different values we raise, otherwise if they all
Expand Down Expand Up @@ -326,15 +348,9 @@ def get_entry_point_from_class(class_module: str, class_name: str) -> Tuple[Opti
:param class_name: name of the class
:return: a tuple of the corresponding group and entry point or None if not found
"""
for group in get_entry_point_groups():
for entry_point in get_entry_points(group):

if entry_point.module != class_module:
continue

if entry_point.attr == class_name:
return group, entry_point

for entry_point in eps():
if entry_point.module == class_module and entry_point.attr == class_name:
return entry_point.group, entry_point
return None, None


Expand Down

0 comments on commit fa00ee6

Please sign in to comment.