From b380a42b587834647080faa44318bc5168f51b47 Mon Sep 17 00:00:00 2001 From: Kristjan Eimre Date: Thu, 2 Nov 2023 12:18:29 +0100 Subject: [PATCH 1/8] rm 3.8 from setup.json --- setup.json | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.json b/setup.json index e7adf5cc..9064bfa2 100644 --- a/setup.json +++ b/setup.json @@ -16,7 +16,6 @@ "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Intended Audience :: Developers", From 22123cd59d45f4b3964a5ffe9dc31136d540d914 Mon Sep 17 00:00:00 2001 From: Kristjan Eimre Date: Thu, 2 Nov 2023 12:20:17 +0100 Subject: [PATCH 2/8] pyupgrade --py37-plus --- aiida_optimade/cli/cmd_init.py | 2 +- aiida_optimade/mappers/entries.py | 2 +- tasks.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aiida_optimade/cli/cmd_init.py b/aiida_optimade/cli/cmd_init.py index e4d65d90..9332a8e2 100644 --- a/aiida_optimade/cli/cmd_init.py +++ b/aiida_optimade/cli/cmd_init.py @@ -153,7 +153,7 @@ def init(obj: "AttributeDict", force: bool, silent: bool, mongo: bool, filename: "consider using --force to first drop the collection, if possible." ) - with open(filename, "r") as handle: + with open(filename) as handle: if silent: all_chunks = read_chunks(handle, chunk_size=chunk_size) else: diff --git a/aiida_optimade/mappers/entries.py b/aiida_optimade/mappers/entries.py index 69feca1c..7cbd94b1 100644 --- a/aiida_optimade/mappers/entries.py +++ b/aiida_optimade/mappers/entries.py @@ -28,7 +28,7 @@ def all_aliases(cls) -> Tuple[Tuple[str, str]]: """Get all aliases as a tuple Also add `PROJECT_PREFIX` fields to the tuple """ - res = super(ResourceMapper, cls).all_aliases() + res = super().all_aliases() return res + tuple( (field, f"{cls.PROJECT_PREFIX}{field}") for field in set(cls.ENTRY_RESOURCE_ATTRIBUTES.keys()) diff --git a/tasks.py b/tasks.py index e4183ba8..6a676d9b 100644 --- a/tasks.py +++ b/tasks.py @@ -6,7 +6,7 @@ def update_file(filename: str, sub_line: Tuple[str, str], strip: str = None): """Utility function for tasks to read, update, and write files""" - with open(filename, "r") as handle: + with open(filename) as handle: lines = [ re.sub(sub_line[0], sub_line[1], line.rstrip(strip)) for line in handle ] From 96e51fca2d0b45e4563b1574399d0b2c15425e9a Mon Sep 17 00:00:00 2001 From: Kristjan Eimre Date: Thu, 2 Nov 2023 12:23:41 +0100 Subject: [PATCH 3/8] 3.8 to 3.9 in CI/CD --- .github/workflows/cd_release.yml | 4 ++-- .github/workflows/ci_automerge_dependabot.yml | 4 ++-- .github/workflows/ci_dependabot.yml | 4 ++-- .github/workflows/ci_tests.yml | 22 +++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/cd_release.yml b/.github/workflows/cd_release.yml index be2ad8fb..0969dc92 100644 --- a/.github/workflows/cd_release.yml +++ b/.github/workflows/cd_release.yml @@ -23,10 +23,10 @@ jobs: with: fetch-depth: 0 - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - name: Install Python dependencies run: | diff --git a/.github/workflows/ci_automerge_dependabot.yml b/.github/workflows/ci_automerge_dependabot.yml index 121425a6..9f04e49d 100644 --- a/.github/workflows/ci_automerge_dependabot.yml +++ b/.github/workflows/ci_automerge_dependabot.yml @@ -30,10 +30,10 @@ jobs: ref: ${{ github.event.pull_request.head.ref }} persist-credentials: false - - name: Setup Python 3.8 + - name: Setup Python 3.9 uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - name: Install Python dependencies run: | diff --git a/.github/workflows/ci_dependabot.yml b/.github/workflows/ci_dependabot.yml index 98f1e9cb..877e783d 100644 --- a/.github/workflows/ci_dependabot.yml +++ b/.github/workflows/ci_dependabot.yml @@ -25,10 +25,10 @@ jobs: with: ref: ${{ env.DEFAULT_REPO_BRANCH }} - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - name: Install `pre-commit` run: | diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 028e4a0b..0ce1ea36 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -17,10 +17,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - name: Install dependencies run: | @@ -45,7 +45,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10'] + python-version: ['3.9', '3.10'] services: mongo: @@ -105,14 +105,14 @@ jobs: run: pytest -v --cov=./aiida_optimade/ --cov-report=xml:mongo_cov.xml --durations=20 - name: Upload coverage to Codecov - if: matrix.python-version == 3.8 + if: matrix.python-version == 3.9 uses: codecov/codecov-action@v3 with: flags: aiida file: ./coverage.xml - name: Upload coverage to Codecov - if: matrix.python-version == 3.8 + if: matrix.python-version == 3.9 uses: codecov/codecov-action@v3 with: flags: mongo @@ -148,10 +148,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - name: Install python dependencies run: | @@ -223,10 +223,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - name: Install python dependencies run: | @@ -292,10 +292,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Python 3.8 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.9 - name: Install python dependencies run: | From 227413ff23ebb02d168327a61677b7da4259f0bb Mon Sep 17 00:00:00 2001 From: Kristjan Eimre Date: Thu, 2 Nov 2023 12:23:54 +0100 Subject: [PATCH 4/8] 3.9 in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 91403769..8fd52fc0 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ long_description=open(MODULE_DIR.joinpath("README.md")).read(), long_description_content_type="text/markdown", packages=find_packages(exclude=["tests", "profiles"]), - python_requires=">=3.8", + python_requires=">=3.9", install_requires=REQUIREMENTS, extras_require={"dev": DEV, "testing": TESTING}, entry_points={ From 6b34ef85dd718fcda221aeef8cadd1af38fa3279 Mon Sep 17 00:00:00 2001 From: Kristjan Eimre Date: Thu, 2 Nov 2023 12:33:36 +0100 Subject: [PATCH 5/8] preliminary readme update --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a471660a..3b33324c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ The compatibility matrix below assumes the user always install the latest patch | Plugin | AiiDA | Python | Specification | |-|-|-|-| -| `v1.0 < v2.0` | ![Compatibility for v1.0][AiiDA v2 range] | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/aiida-optimade)](https://pypi.org/project/aiida-optimade) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | +| `v1.2 < v2.0` | ![Compatibility for v1.0][AiiDA v2 range] | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/aiida-optimade)](https://pypi.org/project/aiida-optimade) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | +| `v1.0 < v1.2` | ![Compatibility for v1.0][AiiDA v2 range] | [![PyPI pyversions][Python v3.8-v3.10]](https://pypi.org/project/aiida-optimade/1.1.1/) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | | `v0.18 <= v0.20` | ![Compatibility for v0][AiiDA v1 range] | [![PyPI pyversions][Python v3.7-v3.9]](https://pypi.org/project/aiida-optimade/0.20.0/) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | | Latest release | Build status | Activity | @@ -175,6 +176,9 @@ For example: `v2.3.12`. [AiiDA v1 range]: https://img.shields.io/badge/AiiDA->=1.6.0,<2.0.0-007ec6.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAhCAYAAABTERJSAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAFhgAABYYBG6Yz4AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAUbSURBVFiFzZhrbFRVEMd%2Fc%2B5uu6UUbIFC%2FUAUVEQCLbQJBIiBDyiImJiIhmohYNCkqJAQxASLF8tDgYRHBLXRhIcKNtFEhVDgAxBJqgmVh4JEKg3EIn2QYqBlt917xg%2BFss%2ByaDHOtzsz5z%2B%2FuZl7ztmF%2F5HJvxVQN6cPYX8%2FPLnOmsvNAvqfwuib%2FbNIk9cQeQnLcKRL5xLIV%2Fic9eJeunjPYbRs4FjQSpTB3aS1IpRKeeOOewajy%2FKKEO8Q0DuVdKy8IqsbPulxGHUfCBBu%2BwUYGuFuBTK7wQnht6PEbf4tlRomVRjCbXNjQEB0AyrFQOL5ENIJm7dTLZE6DPJCnEtFZVXDLny%2B4Sjv0PmmYu1ZdUek9RiMgoDmJ8V0L7XJqsZ3UW8YsBOwEeHeeFce7jEYXBy0m9m4BbXqSj2%2Bxnkg26MCVrN6DEZcwggtd8pTFx%2Fh3B9B50YLaFOPwXQKUt0tBLegtSomfBlfY13PwijbEnhztGzgJsK5h9W9qeWwBqjvyhB2iBs1Qz0AU974DciRGO8CVN8AJhAeMAdA3KbrKEtvxhsI%2B9emWiJlGBEU680Cfk%2BSsVqXZvcFYGXjF8ABVJ%2BTNfVXehyms1zzn1gmIOxLEB6E31%2FWBe5rnCarmo7elf7dJEeaLh80GasliI5F6Q9cAz1GY1OJVNDxTzQTw7iY%2FHEZRQY7xqJ9RU2LFe%2FYqakdP911ha0XhjjiTVAkDwgatWfCGeYocx8M3glG8g8EXhSrLrHnEFJ5Ymow%2FkhIYv6ttYUW1iFmEqqxdVoUs9FmsDYSqmtmJh3Cl1%2BVtl2s7owDUdocR5bceiyoSivGTT5vzpbzL1uoBpmcAAQgW7ArnKD9ng9rc%2BNgrobSNwpSkkhcRN%2BvmXLjIsDovYHHEfmsYFygPAnIDEQrQPzJYCOaLHLUfIt7Oq0LJn9fxkSgNCb1qEIQ5UKgT%2Fs6gJmVOOroJhQBXVqw118QtWLdyUxEP45sUpSzqP7RDdFYMyB9UReMiF1MzPwoUqHt8hjGFFeP5wZAbZ%2F0%2BcAtAAcji6LeSq%2FMYiAvSsdw3GtrfVSVFUBbIhwRWYR7yOcr%2FBi%2FB1MSJZ16JlgH1AGM3EO2QnmMyrSbTSiACgFBv4yCUapZkt9qwWVL7aeOyHvArJjm8%2Fz9BhdI4XcZgz2%2FvRALosjsk1ODOyMcJn9%2FYI6IrkS5vxMGdUwou2YKfyVqJpn5t9aNs3gbQMbdbkxnGdsr4bTHm2AxWo9yNZK4PXR3uzhAh%2BM0AZejnCrGdy0UvJxl0oMKgWSLR%2B1LH2aE9ViejiFs%2BXn6bTjng3MlIhJ1I1TkuLdg6OcAbD7Xx%2Bc3y9TrWAiSHqVkbZ2v9ilCo6s4AjwZCzFyD9mOL305nV9aonvsQeT2L0gVk4OwOJqXXVRW7naaxswDKVdlYLyMXAnntteYmws2xcVVZzq%2BtHPAooQggmJkc6TLSusOiL4RKgwzzYU1iFQgiUBA1H7E8yPau%2BZl9P7AblVNebtHqTgxLfRqrNvZWjsHZFuqMqKcDWdlFjF7UGvX8Jn24DyEAykJwNcdg0OvJ4p5pQ9tV6SMlP4A0PNh8aYze1ArROyUNTNouy8tNF3Rt0CSXb6bRFl4%2FIfQzNMjaE9WwpYOWQnOdEF%2BTdJNO0iFh7%2BI0kfORzQZb6P2kymS9oTxzBiM9rUqLWr1WE5G6ODhycQd%2FUnNVeMbcH68hYkGycNoUNWc8fxaxfwhDbHpfwM5oeTY7rUX8QAAAABJRU5ErkJggg%3D%3D + +[Python v3.8-v3.10]: https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10-blue + [Python v3.7-v3.9]: https://img.shields.io/badge/python-3.7%20%7C%203.8%20%7C%203.9-blue [OPTIMADE from OPT]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Materials-Consortia/optimade-python-tools/v0.24.1/optimade-version.json From 2bf5e9515d48afa5941f6f4a97f9d07f2cd37524 Mon Sep 17 00:00:00 2001 From: Kristjan Eimre Date: Thu, 2 Nov 2023 12:35:49 +0100 Subject: [PATCH 6/8] add support for 3.11 --- .github/workflows/ci_tests.yml | 2 +- setup.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 0ce1ea36..673fa152 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -45,7 +45,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.9', '3.10'] + python-version: ['3.9', '3.10', '3.11'] services: mongo: diff --git a/setup.json b/setup.json index 9064bfa2..c9d2aa3c 100644 --- a/setup.json +++ b/setup.json @@ -18,6 +18,7 @@ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Intended Audience :: Developers", "Topic :: Database", "Topic :: Database :: Database Engines/Servers", From 54baee72965dda0a1402b7fc33cec7df422544b8 Mon Sep 17 00:00:00 2001 From: Kristjan Eimre Date: Mon, 6 Nov 2023 12:49:14 +0100 Subject: [PATCH 7/8] pyupgrade --py39-plus --- aiida_optimade/cli/cmd_init.py | 3 +- aiida_optimade/entry_collections.py | 36 ++++++++++++------------ aiida_optimade/mappers/entries.py | 12 ++++---- aiida_optimade/mappers/structures.py | 3 +- aiida_optimade/translators/entities.py | 4 +-- aiida_optimade/translators/structures.py | 20 ++++++------- aiida_optimade/translators/utils.py | 14 ++++----- aiida_optimade/utils.py | 4 +-- tasks.py | 3 +- tests/cli/conftest.py | 7 ++--- tests/server/conftest.py | 3 +- tests/server/test_entry_collections.py | 4 +-- tests/server/utils.py | 3 +- 13 files changed, 57 insertions(+), 59 deletions(-) diff --git a/aiida_optimade/cli/cmd_init.py b/aiida_optimade/cli/cmd_init.py index 9332a8e2..e590e369 100644 --- a/aiida_optimade/cli/cmd_init.py +++ b/aiida_optimade/cli/cmd_init.py @@ -9,7 +9,8 @@ from aiida_optimade.common.logger import LOGGER, disable_logging if TYPE_CHECKING: # pragma: no cover - from typing import IO, Generator, Iterator, List, Union + from collections.abc import Generator, Iterator + from typing import IO, List, Union from aiida.common.extendeddicts import AttributeDict diff --git a/aiida_optimade/entry_collections.py b/aiida_optimade/entry_collections.py index 8a43098b..8d0c9807 100644 --- a/aiida_optimade/entry_collections.py +++ b/aiida_optimade/entry_collections.py @@ -1,5 +1,5 @@ import warnings -from typing import Any, Dict, List, Optional, Set, Tuple, Union +from typing import Any, Optional, Union from aiida.orm import Group from aiida.orm.nodes import Node @@ -31,7 +31,7 @@ class AiidaCollection(EntryCollection): def __init__( self, - entities: Union[str, List[str]], + entities: Union[str, list[str]], group: Optional[str], resource_cls: EntryResource, resource_mapper: ResourceMapper, @@ -48,15 +48,15 @@ def __init__( # "Cache" self._data_available: int = None self._data_returned: int = None - self._extras_fields: Set[str] = None - self._latest_filter: Dict[str, Any] = None - self._count: Dict[str, Any] = None + self._extras_fields: set[str] = None + self._latest_filter: dict[str, Any] = None + self._count: dict[str, Any] = None self._checked_extras_filter_fields: set = set() - self._all_fields: Set[str] = None + self._all_fields: set[str] = None @property - def all_fields(self) -> Set[str]: + def all_fields(self) -> set[str]: if not self._all_fields: self._all_fields = super().all_fields return self._all_fields @@ -114,7 +114,7 @@ def _clear_cache(self) -> None: def __len__(self) -> int: return self.data_available - def insert(self, _: List[EntryResource]) -> None: + def insert(self, _: list[EntryResource]) -> None: raise NotImplementedError( f"The insert method is not implemented for {self.__class__.__name__}." ) @@ -175,8 +175,8 @@ def count(self, **kwargs) -> int: def find( # pylint: disable=too-many-branches self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] - ) -> Tuple[ - Union[List[EntryResource], EntryResource, None], int, bool, Set[str], Set[str] + ) -> tuple[ + Union[list[EntryResource], EntryResource, None], int, bool, set[str], set[str] ]: self.set_data_available() @@ -266,8 +266,8 @@ def find( # pylint: disable=too-many-branches ) def _run_db_query( - self, criteria: Dict[str, Any], single_entry: bool = False - ) -> Tuple[List[Dict[str, Any]], bool]: + self, criteria: dict[str, Any], single_entry: bool = False + ) -> tuple[list[dict[str, Any]], bool]: """Run the query on the backend and collect the results. Arguments: @@ -296,7 +296,7 @@ def _run_db_query( @staticmethod def _prepare_query( - node_types: List[str], group: Optional[str] = None, **kwargs + node_types: list[str], group: Optional[str] = None, **kwargs ) -> QueryBuilder: """Workhorse function to prepare an AiiDA QueryBuilder query""" for key in kwargs: @@ -344,7 +344,7 @@ def _perform_count(self, **kwargs) -> int: def handle_query_params( self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by AiiDA's QueryBuilder. @@ -385,7 +385,7 @@ def handle_query_params( return cursor_kwargs - def parse_sort_params(self, sort_params: str) -> List[Dict[str, Dict[str, str]]]: + def parse_sort_params(self, sort_params: str) -> list[dict[str, dict[str, str]]]: """Handles any sort parameters passed to the collection, resolving aliases and dealing with any invalid fields. @@ -473,8 +473,8 @@ def __filter_fields_util( # pylint: disable=unused-private-member __filter_fields_util(deepcopy(filters)) def _check_and_calculate_entities( - self, cli: bool = False, entries: List[List[int]] = None - ) -> List[int]: + self, cli: bool = False, entries: list[list[int]] = None + ) -> list[int]: """Check all entities have OPTIMADE extras, else calculate them For a bit of optimization, we only care about a field if it has specifically @@ -490,7 +490,7 @@ def _check_and_calculate_entities( """ - def _update_entities(entities: List[List[Any]], fields: List[str]): + def _update_entities(entities: list[list[Any]], fields: list[str]): """Utility function to update entities within this method""" optimade_fields = [ self.resource_mapper.get_optimade_field(_) for _ in fields diff --git a/aiida_optimade/mappers/entries.py b/aiida_optimade/mappers/entries.py index 7cbd94b1..d98fe465 100644 --- a/aiida_optimade/mappers/entries.py +++ b/aiida_optimade/mappers/entries.py @@ -1,5 +1,5 @@ # pylint: disable=arguments-differ -from typing import Any, Dict, Set, Tuple +from typing import Any from optimade.server.mappers import BaseResourceMapper as OptimadeResourceMapper @@ -13,9 +13,9 @@ class ResourceMapper(OptimadeResourceMapper): PROJECT_PREFIX: str = "extras.optimade." - TRANSLATORS: Dict[str, AiidaEntityTranslator] - REQUIRED_ATTRIBUTES: Set[str] = set() - TOP_LEVEL_NON_ATTRIBUTES_FIELDS: Set[str] = { + TRANSLATORS: dict[str, AiidaEntityTranslator] + REQUIRED_ATTRIBUTES: set[str] = set() + TOP_LEVEL_NON_ATTRIBUTES_FIELDS: set[str] = { "id", "type", "relationships", @@ -24,7 +24,7 @@ class ResourceMapper(OptimadeResourceMapper): } @classmethod - def all_aliases(cls) -> Tuple[Tuple[str, str]]: + def all_aliases(cls) -> tuple[tuple[str, str]]: """Get all aliases as a tuple Also add `PROJECT_PREFIX` fields to the tuple """ @@ -37,7 +37,7 @@ def all_aliases(cls) -> Tuple[Tuple[str, str]]: ) @classmethod - def map_back(cls, entity_properties: Dict[str, Any]) -> dict: + def map_back(cls, entity_properties: dict[str, Any]) -> dict: """Map properties from AiiDA to OPTIMADE Parameters: diff --git a/aiida_optimade/mappers/structures.py b/aiida_optimade/mappers/structures.py index 2ad36b5a..c7f67b41 100644 --- a/aiida_optimade/mappers/structures.py +++ b/aiida_optimade/mappers/structures.py @@ -1,5 +1,4 @@ import warnings -from typing import Dict from optimade.server.config import CONFIG, SupportedBackend @@ -19,7 +18,7 @@ class StructureMapper(ResourceMapper): """Map 'structure' resources from OPTIMADE to AiiDA""" - TRANSLATORS: Dict[str, AiidaEntityTranslator] = { + TRANSLATORS: dict[str, AiidaEntityTranslator] = { "data.core.cif.CifData.": CifDataTranslator, "data.core.structure.StructureData.": StructureDataTranslator, } diff --git a/aiida_optimade/translators/entities.py b/aiida_optimade/translators/entities.py index 0a90d21c..3fd233e1 100644 --- a/aiida_optimade/translators/entities.py +++ b/aiida_optimade/translators/entities.py @@ -1,4 +1,4 @@ -from typing import Any, List, Union +from typing import Any, Union from aiida import orm from aiida.orm.nodes import Node @@ -25,7 +25,7 @@ def __init__(self, pk: int): self.__node = None def _get_unique_node_property( - self, project: Union[List[str], str] + self, project: Union[list[str], str] ) -> Union[Node, Any]: query = QueryBuilder(limit=1) query.append(self.AIIDA_ENTITY, filters={"id": self._pk}, project=project) diff --git a/aiida_optimade/translators/structures.py b/aiida_optimade/translators/structures.py index 95a792e4..665ddfea 100644 --- a/aiida_optimade/translators/structures.py +++ b/aiida_optimade/translators/structures.py @@ -1,7 +1,7 @@ # pylint: disable=line-too-long,too-many-public-methods import itertools from math import fsum -from typing import Any, List, Union +from typing import Any, Union from aiida.orm.nodes.data.structure import StructureData from optimade.models.utils import ANONYMOUS_ELEMENTS @@ -138,7 +138,7 @@ def has_partial_occupancy(self) -> bool: return False # Start creating fields - def elements(self) -> List[str]: + def elements(self) -> list[str]: """Names of elements found in the structure as a list of strings, in alphabetical order.""" attribute = "elements" @@ -168,7 +168,7 @@ def nelements(self) -> int: self.new_attributes[attribute] = res return res - def elements_ratios(self) -> List[float]: + def elements_ratios(self) -> list[float]: """Relative proportions of different elements in the structure.""" attribute = "elements_ratios" @@ -281,7 +281,7 @@ def chemical_formula_anonymous(self) -> str: self.new_attributes[attribute] = res return res - def dimension_types(self) -> List[int]: + def dimension_types(self) -> list[int]: """List of three integers. For each of the three directions indicated by the three lattice vectors @@ -313,7 +313,7 @@ def nperiodic_dimensions(self) -> int: self.new_attributes[attribute] = res return res - def lattice_vectors(self) -> List[List[float]]: + def lattice_vectors(self) -> list[list[float]]: """The three lattice vectors in Cartesian coordinates, in ångström (Å).""" attribute = "lattice_vectors" @@ -326,7 +326,7 @@ def lattice_vectors(self) -> List[List[float]]: self.new_attributes[attribute] = floats_to_hex(res) return res - def cartesian_site_positions(self) -> List[List[Union[float, None]]]: + def cartesian_site_positions(self) -> list[list[Union[float, None]]]: """Cartesian positions of each site. A site is an atom, a site potentially occupied by an atom, @@ -357,7 +357,7 @@ def nsites(self) -> int: self.new_attributes[attribute] = res return res - def species_at_sites(self) -> List[str]: + def species_at_sites(self) -> list[str]: """Name of the species at each site (Where values for sites are specified with the same order of the property @@ -374,7 +374,7 @@ def species_at_sites(self) -> List[str]: self.new_attributes[attribute] = res return res - def species(self) -> List[dict]: + def species(self) -> list[dict]: """A list describing the species of the sites of this structure. Species can be pure chemical elements, or virtual-crystal atoms @@ -427,7 +427,7 @@ def species(self) -> List[dict]: self.new_attributes[attribute] = res return res - def assemblies(self) -> Union[List[dict], None]: + def assemblies(self) -> Union[list[dict], None]: """A description of groups of sites that are statistically correlated. NOTE: Currently not supported. @@ -443,7 +443,7 @@ def assemblies(self) -> Union[List[dict], None]: self.new_attributes[attribute] = res return res - def structure_features(self) -> List[str]: + def structure_features(self) -> list[str]: """A list of strings that flag which special features are used by the structure. SHOULD be absent if there are no partial occupancies diff --git a/aiida_optimade/translators/utils.py b/aiida_optimade/translators/utils.py index 8502b195..cbb8c66d 100644 --- a/aiida_optimade/translators/utils.py +++ b/aiida_optimade/translators/utils.py @@ -1,11 +1,11 @@ -from typing import List, Union +from typing import Union __all__ = ("hex_to_floats",) def check_floating_round_errors( - some_list: List[Union[List[float], float]] -) -> List[Union[List[float], float]]: + some_list: list[Union[list[float], float]] +) -> list[Union[list[float], float]]: """Check whether there are some float rounding errors (check only for close to zero numbers) @@ -28,8 +28,8 @@ def check_floating_round_errors( def floats_to_hex( - some_list: List[Union[List[float], float]] -) -> List[Union[List[str], str]]: + some_list: list[Union[list[float], float]] +) -> list[Union[list[str], str]]: """Convert floats embedded in lists to hex strings (for storing "precise" floats) :param some_list: Must be a list of either lists or float values @@ -53,8 +53,8 @@ def floats_to_hex( def hex_to_floats( - some_list: List[Union[List[str], str]] -) -> List[Union[List[float], float]]: + some_list: list[Union[list[str], str]] +) -> list[Union[list[float], float]]: """Convert hex strings embedded in lists (back) to floats :param some_list: Must be a list of either lists or string values diff --git a/aiida_optimade/utils.py b/aiida_optimade/utils.py index e40534ce..3bca6f66 100644 --- a/aiida_optimade/utils.py +++ b/aiida_optimade/utils.py @@ -1,5 +1,3 @@ -from typing import Tuple - from optimade.models import DataType OPEN_API_ENDPOINTS = { @@ -11,7 +9,7 @@ def retrieve_queryable_properties( schema: dict, queryable_properties: list -) -> Tuple[dict, dict]: +) -> tuple[dict, dict]: """Get all queryable properties from an OPTIMADE schema""" properties = {} all_properties = {} diff --git a/tasks.py b/tasks.py index 6a676d9b..100ff156 100644 --- a/tasks.py +++ b/tasks.py @@ -1,10 +1,9 @@ import re -from typing import Tuple from invoke import task -def update_file(filename: str, sub_line: Tuple[str, str], strip: str = None): +def update_file(filename: str, sub_line: tuple[str, str], strip: str = None): """Utility function for tasks to read, update, and write files""" with open(filename) as handle: lines = [ diff --git a/tests/cli/conftest.py b/tests/cli/conftest.py index be3fcfbb..0379dc00 100644 --- a/tests/cli/conftest.py +++ b/tests/cli/conftest.py @@ -4,7 +4,6 @@ import signal from subprocess import PIPE, Popen, TimeoutExpired from time import sleep -from typing import List, Tuple import click import pytest @@ -28,7 +27,7 @@ def run_cli_command(aiida_test_profile: str): from click.testing import Result def _run_cli_command( - command: click.Command, options: List[str] = None, raises: bool = False + command: click.Command, options: list[str] = None, raises: bool = False ) -> Result: """Run the command and check the result. @@ -76,8 +75,8 @@ def run_and_terminate_server(aiida_test_profile: str): """ def _run_and_terminate_server( - command: str, options: List[str] = None - ) -> Tuple[str, str]: + command: str, options: list[str] = None + ) -> tuple[str, str]: """Run the command and check the result. Note, the `output_lines` attribute is added to return value containing list of diff --git a/tests/server/conftest.py b/tests/server/conftest.py index 39fd22c7..b436d161 100644 --- a/tests/server/conftest.py +++ b/tests/server/conftest.py @@ -5,7 +5,8 @@ import pytest if TYPE_CHECKING: - from typing import Any, Callable, Dict, Iterable, List, Optional, Union + from collections.abc import Iterable + from typing import Any, Callable, Dict, List, Optional, Union from httpx import Response diff --git a/tests/server/test_entry_collections.py b/tests/server/test_entry_collections.py index 04dbfae3..093edacb 100644 --- a/tests/server/test_entry_collections.py +++ b/tests/server/test_entry_collections.py @@ -1,6 +1,6 @@ """Tests for aiida_optimade.entry_collections.""" # pylint: disable=protected-access -from typing import Any, Callable, Dict +from typing import Any, Callable import pytest @@ -29,7 +29,7 @@ def test_causation_errors(attribute: str): def test_bad_fields( - get_good_response: Callable[[str], Dict[str, Any]], + get_good_response: Callable[[str], dict[str, Any]], check_error_response: Callable[[str, int, str, str], None], ): """Test a UnknownProviderProperty warning is emitted for unrecognized provider diff --git a/tests/server/utils.py b/tests/server/utils.py index 5434e508..5d34572f 100644 --- a/tests/server/utils.py +++ b/tests/server/utils.py @@ -14,7 +14,8 @@ from pydantic import BaseModel if TYPE_CHECKING: - from typing import Any, Dict, Iterable, Optional, Type, Union + from collections.abc import Iterable + from typing import Any, Dict, Optional, Type, Union import httpx from starlette import testclient, types From 6fba496202b4353a6ac9184a79b515151f38101f Mon Sep 17 00:00:00 2001 From: Kristjan Eimre Date: Mon, 6 Nov 2023 12:59:05 +0100 Subject: [PATCH 8/8] update readme compatibility matrix --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3b33324c..88189dde 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ The compatibility matrix below assumes the user always install the latest patch | Plugin | AiiDA | Python | Specification | |-|-|-|-| -| `v1.2 < v2.0` | ![Compatibility for v1.0][AiiDA v2 range] | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/aiida-optimade)](https://pypi.org/project/aiida-optimade) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | -| `v1.0 < v1.2` | ![Compatibility for v1.0][AiiDA v2 range] | [![PyPI pyversions][Python v3.8-v3.10]](https://pypi.org/project/aiida-optimade/1.1.1/) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | +| `v1.2 < v2.0` | ![Compatibility for v1.0][AiiDA v2 py311 range] | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/aiida-optimade)](https://pypi.org/project/aiida-optimade) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | +| `v1.0 < v1.2` | ![Compatibility for v1.0][AiiDA v2 py38 range] | [![PyPI pyversions][Python v3.8-v3.10]](https://pypi.org/project/aiida-optimade/1.1.1/) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | | `v0.18 <= v0.20` | ![Compatibility for v0][AiiDA v1 range] | [![PyPI pyversions][Python v3.7-v3.9]](https://pypi.org/project/aiida-optimade/0.20.0/) | ![OPTIMADE API compatibility][OPTIMADE from OPT] | | Latest release | Build status | Activity | @@ -172,7 +172,9 @@ The release action will be triggered by newly created release. Note, the tag should start with a `v` and be followed by a full semantic version (see [SemVer](https://semver.org)). For example: `v2.3.12`. -[AiiDA v2 range]: https://img.shields.io/badge/AiiDA->=2.0.0,<3.0.0-007ec6.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAhCAYAAABTERJSAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAFhgAABYYBG6Yz4AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAUbSURBVFiFzZhrbFRVEMd%2Fc%2B5uu6UUbIFC%2FUAUVEQCLbQJBIiBDyiImJiIhmohYNCkqJAQxASLF8tDgYRHBLXRhIcKNtFEhVDgAxBJqgmVh4JEKg3EIn2QYqBlt917xg%2BFss%2ByaDHOtzsz5z%2B%2FuZl7ztmF%2F5HJvxVQN6cPYX8%2FPLnOmsvNAvqfwuib%2FbNIk9cQeQnLcKRL5xLIV%2Fic9eJeunjPYbRs4FjQSpTB3aS1IpRKeeOOewajy%2FKKEO8Q0DuVdKy8IqsbPulxGHUfCBBu%2BwUYGuFuBTK7wQnht6PEbf4tlRomVRjCbXNjQEB0AyrFQOL5ENIJm7dTLZE6DPJCnEtFZVXDLny%2B4Sjv0PmmYu1ZdUek9RiMgoDmJ8V0L7XJqsZ3UW8YsBOwEeHeeFce7jEYXBy0m9m4BbXqSj2%2Bxnkg26MCVrN6DEZcwggtd8pTFx%2Fh3B9B50YLaFOPwXQKUt0tBLegtSomfBlfY13PwijbEnhztGzgJsK5h9W9qeWwBqjvyhB2iBs1Qz0AU974DciRGO8CVN8AJhAeMAdA3KbrKEtvxhsI%2B9emWiJlGBEU680Cfk%2BSsVqXZvcFYGXjF8ABVJ%2BTNfVXehyms1zzn1gmIOxLEB6E31%2FWBe5rnCarmo7elf7dJEeaLh80GasliI5F6Q9cAz1GY1OJVNDxTzQTw7iY%2FHEZRQY7xqJ9RU2LFe%2FYqakdP911ha0XhjjiTVAkDwgatWfCGeYocx8M3glG8g8EXhSrLrHnEFJ5Ymow%2FkhIYv6ttYUW1iFmEqqxdVoUs9FmsDYSqmtmJh3Cl1%2BVtl2s7owDUdocR5bceiyoSivGTT5vzpbzL1uoBpmcAAQgW7ArnKD9ng9rc%2BNgrobSNwpSkkhcRN%2BvmXLjIsDovYHHEfmsYFygPAnIDEQrQPzJYCOaLHLUfIt7Oq0LJn9fxkSgNCb1qEIQ5UKgT%2Fs6gJmVOOroJhQBXVqw118QtWLdyUxEP45sUpSzqP7RDdFYMyB9UReMiF1MzPwoUqHt8hjGFFeP5wZAbZ%2F0%2BcAtAAcji6LeSq%2FMYiAvSsdw3GtrfVSVFUBbIhwRWYR7yOcr%2FBi%2FB1MSJZ16JlgH1AGM3EO2QnmMyrSbTSiACgFBv4yCUapZkt9qwWVL7aeOyHvArJjm8%2Fz9BhdI4XcZgz2%2FvRALosjsk1ODOyMcJn9%2FYI6IrkS5vxMGdUwou2YKfyVqJpn5t9aNs3gbQMbdbkxnGdsr4bTHm2AxWo9yNZK4PXR3uzhAh%2BM0AZejnCrGdy0UvJxl0oMKgWSLR%2B1LH2aE9ViejiFs%2BXn6bTjng3MlIhJ1I1TkuLdg6OcAbD7Xx%2Bc3y9TrWAiSHqVkbZ2v9ilCo6s4AjwZCzFyD9mOL305nV9aonvsQeT2L0gVk4OwOJqXXVRW7naaxswDKVdlYLyMXAnntteYmws2xcVVZzq%2BtHPAooQggmJkc6TLSusOiL4RKgwzzYU1iFQgiUBA1H7E8yPau%2BZl9P7AblVNebtHqTgxLfRqrNvZWjsHZFuqMqKcDWdlFjF7UGvX8Jn24DyEAykJwNcdg0OvJ4p5pQ9tV6SMlP4A0PNh8aYze1ArROyUNTNouy8tNF3Rt0CSXb6bRFl4%2FIfQzNMjaE9WwpYOWQnOdEF%2BTdJNO0iFh7%2BI0kfORzQZb6P2kymS9oTxzBiM9rUqLWr1WE5G6ODhycQd%2FUnNVeMbcH68hYkGycNoUNWc8fxaxfwhDbHpfwM5oeTY7rUX8QAAAABJRU5ErkJggg%3D%3D +[AiiDA v2 py311 range]: https://img.shields.io/badge/AiiDA->=2.2.0,<3.0.0-007ec6.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAhCAYAAABTERJSAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAFhgAABYYBG6Yz4AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAUbSURBVFiFzZhrbFRVEMd%2Fc%2B5uu6UUbIFC%2FUAUVEQCLbQJBIiBDyiImJiIhmohYNCkqJAQxASLF8tDgYRHBLXRhIcKNtFEhVDgAxBJqgmVh4JEKg3EIn2QYqBlt917xg%2BFss%2ByaDHOtzsz5z%2B%2FuZl7ztmF%2F5HJvxVQN6cPYX8%2FPLnOmsvNAvqfwuib%2FbNIk9cQeQnLcKRL5xLIV%2Fic9eJeunjPYbRs4FjQSpTB3aS1IpRKeeOOewajy%2FKKEO8Q0DuVdKy8IqsbPulxGHUfCBBu%2BwUYGuFuBTK7wQnht6PEbf4tlRomVRjCbXNjQEB0AyrFQOL5ENIJm7dTLZE6DPJCnEtFZVXDLny%2B4Sjv0PmmYu1ZdUek9RiMgoDmJ8V0L7XJqsZ3UW8YsBOwEeHeeFce7jEYXBy0m9m4BbXqSj2%2Bxnkg26MCVrN6DEZcwggtd8pTFx%2Fh3B9B50YLaFOPwXQKUt0tBLegtSomfBlfY13PwijbEnhztGzgJsK5h9W9qeWwBqjvyhB2iBs1Qz0AU974DciRGO8CVN8AJhAeMAdA3KbrKEtvxhsI%2B9emWiJlGBEU680Cfk%2BSsVqXZvcFYGXjF8ABVJ%2BTNfVXehyms1zzn1gmIOxLEB6E31%2FWBe5rnCarmo7elf7dJEeaLh80GasliI5F6Q9cAz1GY1OJVNDxTzQTw7iY%2FHEZRQY7xqJ9RU2LFe%2FYqakdP911ha0XhjjiTVAkDwgatWfCGeYocx8M3glG8g8EXhSrLrHnEFJ5Ymow%2FkhIYv6ttYUW1iFmEqqxdVoUs9FmsDYSqmtmJh3Cl1%2BVtl2s7owDUdocR5bceiyoSivGTT5vzpbzL1uoBpmcAAQgW7ArnKD9ng9rc%2BNgrobSNwpSkkhcRN%2BvmXLjIsDovYHHEfmsYFygPAnIDEQrQPzJYCOaLHLUfIt7Oq0LJn9fxkSgNCb1qEIQ5UKgT%2Fs6gJmVOOroJhQBXVqw118QtWLdyUxEP45sUpSzqP7RDdFYMyB9UReMiF1MzPwoUqHt8hjGFFeP5wZAbZ%2F0%2BcAtAAcji6LeSq%2FMYiAvSsdw3GtrfVSVFUBbIhwRWYR7yOcr%2FBi%2FB1MSJZ16JlgH1AGM3EO2QnmMyrSbTSiACgFBv4yCUapZkt9qwWVL7aeOyHvArJjm8%2Fz9BhdI4XcZgz2%2FvRALosjsk1ODOyMcJn9%2FYI6IrkS5vxMGdUwou2YKfyVqJpn5t9aNs3gbQMbdbkxnGdsr4bTHm2AxWo9yNZK4PXR3uzhAh%2BM0AZejnCrGdy0UvJxl0oMKgWSLR%2B1LH2aE9ViejiFs%2BXn6bTjng3MlIhJ1I1TkuLdg6OcAbD7Xx%2Bc3y9TrWAiSHqVkbZ2v9ilCo6s4AjwZCzFyD9mOL305nV9aonvsQeT2L0gVk4OwOJqXXVRW7naaxswDKVdlYLyMXAnntteYmws2xcVVZzq%2BtHPAooQggmJkc6TLSusOiL4RKgwzzYU1iFQgiUBA1H7E8yPau%2BZl9P7AblVNebtHqTgxLfRqrNvZWjsHZFuqMqKcDWdlFjF7UGvX8Jn24DyEAykJwNcdg0OvJ4p5pQ9tV6SMlP4A0PNh8aYze1ArROyUNTNouy8tNF3Rt0CSXb6bRFl4%2FIfQzNMjaE9WwpYOWQnOdEF%2BTdJNO0iFh7%2BI0kfORzQZb6P2kymS9oTxzBiM9rUqLWr1WE5G6ODhycQd%2FUnNVeMbcH68hYkGycNoUNWc8fxaxfwhDbHpfwM5oeTY7rUX8QAAAABJRU5ErkJggg%3D%3D + +[AiiDA v2 py38 range]: https://img.shields.io/badge/AiiDA->=2.0.0,<2.4.0-007ec6.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAhCAYAAABTERJSAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAFhgAABYYBG6Yz4AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAUbSURBVFiFzZhrbFRVEMd%2Fc%2B5uu6UUbIFC%2FUAUVEQCLbQJBIiBDyiImJiIhmohYNCkqJAQxASLF8tDgYRHBLXRhIcKNtFEhVDgAxBJqgmVh4JEKg3EIn2QYqBlt917xg%2BFss%2ByaDHOtzsz5z%2B%2FuZl7ztmF%2F5HJvxVQN6cPYX8%2FPLnOmsvNAvqfwuib%2FbNIk9cQeQnLcKRL5xLIV%2Fic9eJeunjPYbRs4FjQSpTB3aS1IpRKeeOOewajy%2FKKEO8Q0DuVdKy8IqsbPulxGHUfCBBu%2BwUYGuFuBTK7wQnht6PEbf4tlRomVRjCbXNjQEB0AyrFQOL5ENIJm7dTLZE6DPJCnEtFZVXDLny%2B4Sjv0PmmYu1ZdUek9RiMgoDmJ8V0L7XJqsZ3UW8YsBOwEeHeeFce7jEYXBy0m9m4BbXqSj2%2Bxnkg26MCVrN6DEZcwggtd8pTFx%2Fh3B9B50YLaFOPwXQKUt0tBLegtSomfBlfY13PwijbEnhztGzgJsK5h9W9qeWwBqjvyhB2iBs1Qz0AU974DciRGO8CVN8AJhAeMAdA3KbrKEtvxhsI%2B9emWiJlGBEU680Cfk%2BSsVqXZvcFYGXjF8ABVJ%2BTNfVXehyms1zzn1gmIOxLEB6E31%2FWBe5rnCarmo7elf7dJEeaLh80GasliI5F6Q9cAz1GY1OJVNDxTzQTw7iY%2FHEZRQY7xqJ9RU2LFe%2FYqakdP911ha0XhjjiTVAkDwgatWfCGeYocx8M3glG8g8EXhSrLrHnEFJ5Ymow%2FkhIYv6ttYUW1iFmEqqxdVoUs9FmsDYSqmtmJh3Cl1%2BVtl2s7owDUdocR5bceiyoSivGTT5vzpbzL1uoBpmcAAQgW7ArnKD9ng9rc%2BNgrobSNwpSkkhcRN%2BvmXLjIsDovYHHEfmsYFygPAnIDEQrQPzJYCOaLHLUfIt7Oq0LJn9fxkSgNCb1qEIQ5UKgT%2Fs6gJmVOOroJhQBXVqw118QtWLdyUxEP45sUpSzqP7RDdFYMyB9UReMiF1MzPwoUqHt8hjGFFeP5wZAbZ%2F0%2BcAtAAcji6LeSq%2FMYiAvSsdw3GtrfVSVFUBbIhwRWYR7yOcr%2FBi%2FB1MSJZ16JlgH1AGM3EO2QnmMyrSbTSiACgFBv4yCUapZkt9qwWVL7aeOyHvArJjm8%2Fz9BhdI4XcZgz2%2FvRALosjsk1ODOyMcJn9%2FYI6IrkS5vxMGdUwou2YKfyVqJpn5t9aNs3gbQMbdbkxnGdsr4bTHm2AxWo9yNZK4PXR3uzhAh%2BM0AZejnCrGdy0UvJxl0oMKgWSLR%2B1LH2aE9ViejiFs%2BXn6bTjng3MlIhJ1I1TkuLdg6OcAbD7Xx%2Bc3y9TrWAiSHqVkbZ2v9ilCo6s4AjwZCzFyD9mOL305nV9aonvsQeT2L0gVk4OwOJqXXVRW7naaxswDKVdlYLyMXAnntteYmws2xcVVZzq%2BtHPAooQggmJkc6TLSusOiL4RKgwzzYU1iFQgiUBA1H7E8yPau%2BZl9P7AblVNebtHqTgxLfRqrNvZWjsHZFuqMqKcDWdlFjF7UGvX8Jn24DyEAykJwNcdg0OvJ4p5pQ9tV6SMlP4A0PNh8aYze1ArROyUNTNouy8tNF3Rt0CSXb6bRFl4%2FIfQzNMjaE9WwpYOWQnOdEF%2BTdJNO0iFh7%2BI0kfORzQZb6P2kymS9oTxzBiM9rUqLWr1WE5G6ODhycQd%2FUnNVeMbcH68hYkGycNoUNWc8fxaxfwhDbHpfwM5oeTY7rUX8QAAAABJRU5ErkJggg%3D%3D [AiiDA v1 range]: https://img.shields.io/badge/AiiDA->=1.6.0,<2.0.0-007ec6.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAhCAYAAABTERJSAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAFhgAABYYBG6Yz4AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAUbSURBVFiFzZhrbFRVEMd%2Fc%2B5uu6UUbIFC%2FUAUVEQCLbQJBIiBDyiImJiIhmohYNCkqJAQxASLF8tDgYRHBLXRhIcKNtFEhVDgAxBJqgmVh4JEKg3EIn2QYqBlt917xg%2BFss%2ByaDHOtzsz5z%2B%2FuZl7ztmF%2F5HJvxVQN6cPYX8%2FPLnOmsvNAvqfwuib%2FbNIk9cQeQnLcKRL5xLIV%2Fic9eJeunjPYbRs4FjQSpTB3aS1IpRKeeOOewajy%2FKKEO8Q0DuVdKy8IqsbPulxGHUfCBBu%2BwUYGuFuBTK7wQnht6PEbf4tlRomVRjCbXNjQEB0AyrFQOL5ENIJm7dTLZE6DPJCnEtFZVXDLny%2B4Sjv0PmmYu1ZdUek9RiMgoDmJ8V0L7XJqsZ3UW8YsBOwEeHeeFce7jEYXBy0m9m4BbXqSj2%2Bxnkg26MCVrN6DEZcwggtd8pTFx%2Fh3B9B50YLaFOPwXQKUt0tBLegtSomfBlfY13PwijbEnhztGzgJsK5h9W9qeWwBqjvyhB2iBs1Qz0AU974DciRGO8CVN8AJhAeMAdA3KbrKEtvxhsI%2B9emWiJlGBEU680Cfk%2BSsVqXZvcFYGXjF8ABVJ%2BTNfVXehyms1zzn1gmIOxLEB6E31%2FWBe5rnCarmo7elf7dJEeaLh80GasliI5F6Q9cAz1GY1OJVNDxTzQTw7iY%2FHEZRQY7xqJ9RU2LFe%2FYqakdP911ha0XhjjiTVAkDwgatWfCGeYocx8M3glG8g8EXhSrLrHnEFJ5Ymow%2FkhIYv6ttYUW1iFmEqqxdVoUs9FmsDYSqmtmJh3Cl1%2BVtl2s7owDUdocR5bceiyoSivGTT5vzpbzL1uoBpmcAAQgW7ArnKD9ng9rc%2BNgrobSNwpSkkhcRN%2BvmXLjIsDovYHHEfmsYFygPAnIDEQrQPzJYCOaLHLUfIt7Oq0LJn9fxkSgNCb1qEIQ5UKgT%2Fs6gJmVOOroJhQBXVqw118QtWLdyUxEP45sUpSzqP7RDdFYMyB9UReMiF1MzPwoUqHt8hjGFFeP5wZAbZ%2F0%2BcAtAAcji6LeSq%2FMYiAvSsdw3GtrfVSVFUBbIhwRWYR7yOcr%2FBi%2FB1MSJZ16JlgH1AGM3EO2QnmMyrSbTSiACgFBv4yCUapZkt9qwWVL7aeOyHvArJjm8%2Fz9BhdI4XcZgz2%2FvRALosjsk1ODOyMcJn9%2FYI6IrkS5vxMGdUwou2YKfyVqJpn5t9aNs3gbQMbdbkxnGdsr4bTHm2AxWo9yNZK4PXR3uzhAh%2BM0AZejnCrGdy0UvJxl0oMKgWSLR%2B1LH2aE9ViejiFs%2BXn6bTjng3MlIhJ1I1TkuLdg6OcAbD7Xx%2Bc3y9TrWAiSHqVkbZ2v9ilCo6s4AjwZCzFyD9mOL305nV9aonvsQeT2L0gVk4OwOJqXXVRW7naaxswDKVdlYLyMXAnntteYmws2xcVVZzq%2BtHPAooQggmJkc6TLSusOiL4RKgwzzYU1iFQgiUBA1H7E8yPau%2BZl9P7AblVNebtHqTgxLfRqrNvZWjsHZFuqMqKcDWdlFjF7UGvX8Jn24DyEAykJwNcdg0OvJ4p5pQ9tV6SMlP4A0PNh8aYze1ArROyUNTNouy8tNF3Rt0CSXb6bRFl4%2FIfQzNMjaE9WwpYOWQnOdEF%2BTdJNO0iFh7%2BI0kfORzQZb6P2kymS9oTxzBiM9rUqLWr1WE5G6ODhycQd%2FUnNVeMbcH68hYkGycNoUNWc8fxaxfwhDbHpfwM5oeTY7rUX8QAAAABJRU5ErkJggg%3D%3D