Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,18 @@ dependencies:
- python>=3.10
- alembic~=1.8
- archive-path~=0.4.2
- asyncssh~=2.21.0
- asyncssh~=2.22.0
- circus~=0.19.0
- click-spinner~=0.1.8
- click<8.3,>=8.1.0
- disk-objectstore~=1.5.0
- docstring_parser
- get-annotations~=0.1
- python-graphviz~=0.19
- plumpy~=0.26.0
- ipython>=7.6
- jedi<0.19
- jinja2~=3.0
- kiwipy[rmq]~=0.9.0
- importlib-metadata~=6.0
- numpy<3,>=1.21
- paramiko~=3.0
- pgsu~=0.3.0
Expand Down
5 changes: 1 addition & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,18 @@ classifiers = [
dependencies = [
'alembic~=1.8',
'archive-path~=0.4.2',
"asyncssh~=2.21.0",
"asyncssh~=2.22.0",
'circus~=0.19.0',
'click-spinner~=0.1.8',
'click>=8.1.0,<8.3',
'disk-objectstore~=1.5.0',
'docstring-parser',
'get-annotations~=0.1;python_version<"3.10"',
'graphviz~=0.19',
'plumpy~=0.26.0',
'ipython>=7.6',
'jedi<0.19',
'jinja2~=3.0',
'kiwipy[rmq]~=0.9.0',
'importlib-metadata~=6.0',
'numpy>=1.21,<3',
'paramiko~=3.0',
'pgsu~=0.3.0',
Expand Down Expand Up @@ -386,7 +384,6 @@ module = [
'bs4.*',
'circus.*',
'flask_restful.*',
'get_annotations.*',
'graphviz.*',
'kiwipy.*',
'mayavi.*',
Expand Down
4 changes: 2 additions & 2 deletions src/aiida/cmdline/params/types/identifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from aiida.plugins.entry_point import get_entry_point_from_string

if t.TYPE_CHECKING:
from importlib_metadata import EntryPoint
from importlib.metadata import EntryPoint

from aiida.orm.utils.loaders import OrmEntityLoader

Expand Down Expand Up @@ -114,7 +114,7 @@ def convert(self, value: t.Any, param: click.Parameter | None, ctx: click.Contex

for entry_point in self._entry_points:
try:
sub_class = entry_point.load() # type: ignore[no-untyped-call]
sub_class = entry_point.load()
except ImportError as exception:
raise RuntimeError(f'failed to load the entry point {entry_point}: {exception}')

Expand Down
8 changes: 4 additions & 4 deletions src/aiida/cmdline/params/types/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from .strings import EntryPointType

if t.TYPE_CHECKING:
from importlib_metadata import EntryPoint
from importlib.metadata import EntryPoint

__all__ = ('PluginParamType',)

Expand Down Expand Up @@ -169,7 +169,7 @@ def shell_complete(
entry_point = entry_points_by_name.get(p)
if entry_point is not None:
try:
plugin = entry_point.load() # type: ignore[no-untyped-call]
plugin = entry_point.load()
if plugin.__doc__:
# extract the initial/summary paragraph (before double newline)
doc_summary = plugin.__doc__.strip().split('\n\n')[0]
Expand Down Expand Up @@ -239,7 +239,7 @@ def convert( # type: ignore[override]
"""Convert the string value to an entry point instance, if the value can be successfully parsed
into an actual entry point. Will raise click.BadParameter if validation fails.
"""
from importlib_metadata import EntryPoint
from importlib.metadata import EntryPoint

# If the value is already of the expected return type, simply return it. This behavior is new in `click==8.0`:
# https://click.palletsprojects.com/en/8.0.x/parameters/#implementing-custom-types
Expand All @@ -260,7 +260,7 @@ def convert( # type: ignore[override]

if self.load:
try:
return entry_point.load() # type: ignore[no-untyped-call]
return entry_point.load()
except exceptions.LoadingEntryPointError as exception:
raise click.BadParameter(str(exception))
else:
Expand Down
20 changes: 3 additions & 17 deletions src/aiida/engine/processes/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
import inspect
import logging
import signal
import types
import typing as t
from typing import TYPE_CHECKING
from inspect import get_annotations
from types import UnionType
from typing import TYPE_CHECKING, ParamSpec

import docstring_parser

Expand All @@ -41,21 +42,6 @@
from .process import Process
from .process_spec import ProcessSpec

try:
UnionType = types.UnionType
except AttributeError:
# This type is not available for Python 3.9 and older
UnionType = None # type: ignore[assignment,misc]

# Fallback for Python 3.9 and older
from typing_extensions import ParamSpec

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing like this: Move TypeAlias import from typing_extensions to typing in aiida/tools/graph/age_entities.py

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I only changed it in src/aiida/engine/processes/functions.py and not in the other file. There will be a larger PR where we will use pyupgrade to automatically update every source file to 3.10 syntax and this will include also changes as removing typing_extensions imports if it is not done automatically by the tool. We want to do this right before the next minor release because it would create a lot of annoying diff noise resulting in a loft of merge conflicts for the patch releases of 2.8.x that still supports 3.9. In src/aiida/engine/processes/functions.py we already changed this part of the code so it will probably anyway result in a merge conflict. Hope this is okay.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, fair enough!


try:
get_annotations = inspect.get_annotations
except AttributeError:
# This is the backport for Python 3.9 and older
from get_annotations import get_annotations # type: ignore[no-redef]

if TYPE_CHECKING:
from .exit_code import ExitCode

Expand Down
4 changes: 2 additions & 2 deletions src/aiida/manage/tests/pytest_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@
import typing as t
import uuid
import warnings
from importlib.metadata import EntryPoint, EntryPoints

import plumpy
import pytest
import wrapt
from importlib_metadata import EntryPoint, EntryPoints

from aiida import plugins
from aiida.common.exceptions import NotExistent
Expand Down Expand Up @@ -844,7 +844,7 @@ def add(

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

def remove(
self, entry_point_string: str | None = None, *, name: str | None = None, group: str | None = None
Expand Down
2 changes: 1 addition & 1 deletion src/aiida/orm/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from . import convert, entities, extras, users

if TYPE_CHECKING:
from importlib_metadata import EntryPoint
from importlib.metadata import EntryPoint

from aiida.orm import Node, User
from aiida.orm.implementation import StorageBackend
Expand Down
2 changes: 1 addition & 1 deletion src/aiida/orm/nodes/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
from .links import NodeLinks

if TYPE_CHECKING:
from importlib_metadata import EntryPoint
from importlib.metadata import EntryPoint

from aiida.common.log import AiidaLoggerType

Expand Down
20 changes: 12 additions & 8 deletions src/aiida/plugins/entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@
from . import factories

if TYPE_CHECKING:
# importlib.metadata was introduced into the standard library in python 3.8,
# but was then updated in python 3.10 to use an improved API.
# So for now we use the backport importlib_metadata package.
from importlib_metadata import EntryPoint, EntryPoints
from importlib.metadata import EntryPoint, EntryPoints

__all__ = ('get_entry_points', 'load_entry_point', 'load_entry_point_from_string', 'parse_entry_point')

Expand All @@ -43,9 +40,16 @@ def eps() -> EntryPoints:
which will always iterate over all entry points since it looks for
possible duplicate entries.
"""
from importlib_metadata import EntryPoints, entry_points

all_eps = entry_points()
import sys
from importlib.metadata import EntryPoints, entry_points

all_eps: list[EntryPoint] | EntryPoints
if sys.version_info >= (3, 12):
all_eps = entry_points()
else:
# Python <3.12: entry_points() returns SelectableGroups (dict subclass)
# that iterates group names, not EntryPoints. Flatten via .values().
all_eps = [ep for group_eps in entry_points().values() for ep in group_eps]

@agoscinski agoscinski Apr 14, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to iterate through this logic multiple times. I think this is the clearest way. Its a bit annoying but I think its worse the dependency drop.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, this is such a mess, how can they be changing the interface in every other python version?!? Agreed that still worth dropping the dependency.

Image

return EntryPoints(sorted(all_eps, key=lambda x: x.group))


Expand Down Expand Up @@ -154,7 +158,7 @@ class EntryPointFormat(enum.Enum):

def parse_entry_point(group: str, spec: str) -> EntryPoint:
"""Return an entry point, given its group and spec (as formatted in the setup)"""
from importlib_metadata import EntryPoint
from importlib.metadata import EntryPoint

name, value = spec.split('=', maxsplit=1)
return EntryPoint(group=group, name=name.strip(), value=value.strip())
Expand Down
2 changes: 1 addition & 1 deletion src/aiida/plugins/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
)

if TYPE_CHECKING:
from importlib_metadata import EntryPoint
from importlib.metadata import EntryPoint

from aiida.brokers import Broker
from aiida.engine import CalcJob, CalcJobImporter, WorkChain
Expand Down
14 changes: 7 additions & 7 deletions src/aiida/tools/pytest_fixtures/entry_points.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

from __future__ import annotations

import importlib.metadata
import typing as t

import importlib_metadata
import pytest


class EntryPointManager:
"""Manager to temporarily add or remove entry points."""

def __init__(self, entry_points: importlib_metadata.EntryPoints):
def __init__(self, entry_points: importlib.metadata.EntryPoints):
self.entry_points = entry_points

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

def eps_select(self, group, name=None) -> importlib_metadata.EntryPoints:
def eps_select(self, group, name=None) -> importlib.metadata.EntryPoints:
if name is None:
return self.eps().select(group=group)
return self.eps().select(group=group, name=name)
Expand Down Expand Up @@ -77,8 +77,8 @@ def add(
value = f'{value.__module__}:{value.__name__}'

group, name = self._validate_entry_point(entry_point_string, group, name)
entry_point = importlib_metadata.EntryPoint(name, value, group)
self.entry_points = importlib_metadata.EntryPoints(self.entry_points + (entry_point,))
entry_point = importlib.metadata.EntryPoint(name=name, value=value, group=group)
self.entry_points = importlib.metadata.EntryPoints([*self.entry_points, entry_point])

def remove(
self, entry_point_string: str | None = None, *, name: str | None = None, group: str | None = None
Expand All @@ -99,7 +99,7 @@ def remove(
self.entry_points[name]
except KeyError:
raise KeyError(f'entry point `{name}` does not exist in group `{group}`.')
self.entry_points = importlib_metadata.EntryPoints(
self.entry_points = importlib.metadata.EntryPoints(
(ep for ep in self.entry_points if not (ep.name == name and ep.group == group))
)

Expand Down
2 changes: 1 addition & 1 deletion tests/orm/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
"""Test for entity fields"""

import sys
from importlib.metadata import entry_points

import pytest
from importlib_metadata import entry_points

from aiida import orm
from aiida.common.pydantic import MetadataField
Expand Down
5 changes: 3 additions & 2 deletions tests/plugins/test_entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
###########################################################################
"""Tests for the :mod:`~aiida.plugins.entry_point` module."""

from importlib.metadata import EntryPoint as EP # noqa: N817
from importlib.metadata import EntryPoints

import pytest
from importlib_metadata import EntryPoint as EP # noqa: N817
from importlib_metadata import EntryPoints

from aiida.common.exceptions import MissingEntryPointError, MultipleEntryPointError
from aiida.common.warnings import AiidaDeprecationWarning
Expand Down
3 changes: 2 additions & 1 deletion tests/test_conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Tests for fixtures in the ``conftest.py``."""

from importlib.metadata import EntryPoint

import pytest
from importlib_metadata import EntryPoint

from aiida.common.exceptions import MissingEntryPointError
from aiida.plugins.entry_point import get_entry_point, load_entry_point
Expand Down
Loading
Loading