diff --git a/CHANGES.rst b/CHANGES.rst index 06f122429..f0cafd81c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -28,6 +28,7 @@ fixes: - fix: add extra_plugin_dir support to FullStackTest (#1726) - fix: add missing py 3.13 in tox (#1731) - fix: add filter to tar extract (#1730) +- refactor: use importlib to find plugins in entry_points (#1669) v6.2.0 (2024-01-01) diff --git a/errbot/repo_manager.py b/errbot/repo_manager.py index 32e085303..0546398e5 100644 --- a/errbot/repo_manager.py +++ b/errbot/repo_manager.py @@ -6,6 +6,7 @@ import tarfile from collections import namedtuple from datetime import datetime, timedelta +from importlib.metadata import distribution from os import path from pathlib import Path from typing import Dict, Generator, List, Optional, Sequence, Tuple @@ -91,40 +92,30 @@ def check_dependencies(req_path: Path) -> Tuple[Optional[str], Sequence[str]]: Or None, [] if everything is OK. """ log.debug("check dependencies of %s", req_path) - # noinspection PyBroadException - try: - from pkg_resources import get_distribution - - missing_pkg = [] - - if not req_path.is_file(): - log.debug("%s has no requirements.txt file", req_path) - return None, missing_pkg - - with req_path.open() as f: - for line in f: - stripped = line.strip() - # skip empty lines. - if not stripped: - continue - - # noinspection PyBroadException - try: - get_distribution(stripped) - except Exception: - missing_pkg.append(stripped) - if missing_pkg: - return ( - f"You need these dependencies for {req_path}: " + ",".join(missing_pkg), - missing_pkg, - ) + missing_pkg = [] + + if not req_path.is_file(): + log.debug("%s has no requirements.txt file", req_path) return None, missing_pkg - except Exception: - log.exception("Problem checking for dependencies.") + + with req_path.open() as f: + for line in f: + stripped = line.strip() + # skip empty lines. + if not stripped: + continue + + # noinspection PyBroadException + try: + distribution(stripped) + except Exception: + missing_pkg.append(stripped) + if missing_pkg: return ( - "You need to have setuptools installed for the dependency check of the plugins", - [], + f"You need these dependencies for {req_path}: " + ",".join(missing_pkg), + missing_pkg, ) + return None, missing_pkg class BotRepoManager(StoreMixin): diff --git a/errbot/utils.py b/errbot/utils.py index 68d7d70de..658e80137 100644 --- a/errbot/utils.py +++ b/errbot/utils.py @@ -1,8 +1,10 @@ import collections import fnmatch +import importlib.metadata import inspect import logging import os +import pathlib import re import sys import time @@ -10,7 +12,6 @@ from platform import system from typing import List, Tuple, Union -import pkg_resources from dulwich import porcelain log = logging.getLogger(__name__) @@ -199,9 +200,31 @@ def collect_roots(base_paths: List, file_sig: str = "*.plug") -> List: def entry_point_plugins(group): paths = [] - for entry_point in pkg_resources.iter_entry_points(group): - ep = next(pkg_resources.iter_entry_points(group, entry_point.name)) - paths.append(f"{ep.dist.module_path}/{entry_point.module_name}") + + eps = importlib.metadata.entry_points() + try: + entry_points = eps.select(group=group) + except AttributeError: + # workaround to support python 3.9 and older + entry_points = eps.get(group, ()) + + for entry_point in entry_points: + module_name = entry_point.module + file_name = module_name.replace(".", "/") + ".py" + try: + files = entry_point.dist.files + except AttributeError: + # workaround to support python 3.9 and older + try: + files = importlib.metadata.distribution(entry_point.name).files + except importlib.metadata.PackageNotFoundError: + # entrypoint is not a distribution, so let's skip looking for files + continue + + for f in files: + if file_name == str(f): + parent = str(pathlib.Path(f).resolve().parent) + paths.append(f"{parent}/{module_name}") return paths diff --git a/tests/utils_test.py b/tests/utils_test.py index fb15c71e9..92ec25bc2 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -1,7 +1,6 @@ # coding=utf-8 import logging import sys - from datetime import timedelta import pytest @@ -11,7 +10,12 @@ from errbot.bootstrap import CORE_STORAGE, bot_config_defaults from errbot.storage import StoreMixin from errbot.storage.base import StoragePluginBase -from errbot.utils import version2tuple, format_timedelta, split_string_after +from errbot.utils import ( + entry_point_plugins, + format_timedelta, + split_string_after, + version2tuple, +) log = logging.getLogger(__name__) @@ -103,3 +107,17 @@ def test_split_string_after_returns_two_chunks_when_chunksize_equals_half_length splitter = split_string_after(str_, int(len(str_) / 2)) split = [chunk for chunk in splitter] assert ["foobar2000", "foobar2000"] == split + + +def test_entry_point_plugins_no_groups(): + result = entry_point_plugins("does_not_exist") + assert [] == result + + +def test_entry_point_plugins_valid_groups(): + results = entry_point_plugins("console_scripts") + match = False + for result in results: + if result.endswith("errbot/errbot.cli"): + match = True + assert match