From 904fdea18e4a0c44ed91a03f53585e6f59840ba6 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Fri, 3 Nov 2023 10:54:28 +0100 Subject: [PATCH] addons: Make sure to iterate over valid entry points I.e. cast pkg_resources.EntryPoint to corresponding importlib.metadata.EntryPoint because the source of these are from downstream clients which might still construct them. --- orangecanvas/application/addons.py | 7 +++--- orangecanvas/application/utils/addons.py | 2 +- orangecanvas/config.py | 7 +++++- orangecanvas/utils/pkgmeta.py | 26 ++++++++++++++++++++++- orangecanvas/utils/tests/test_pkgmeta.py | 27 ++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 orangecanvas/utils/tests/test_pkgmeta.py diff --git a/orangecanvas/application/addons.py b/orangecanvas/application/addons.py index 820e6cb2..7a59be33 100644 --- a/orangecanvas/application/addons.py +++ b/orangecanvas/application/addons.py @@ -492,8 +492,7 @@ def start(self, config): self.__config = config if self.__packages is not None: - # method_queued(self.setItems, (object,))(self.__packages) - installed = [ep.dist for ep in config.addon_entry_points() + installed = [ep.dist for ep in config._ensure_valid_entry_points() if ep.dist is not None] items = installable_items(self.__packages, installed) self.setItems(items) @@ -548,7 +547,7 @@ def network_warning(exc): network_warning(exc[0]) assert all(isinstance(p, Installable) for p in packages) AddonManagerDialog.__packages = packages - installed = [ep.dist for ep in config.addon_entry_points() + installed = [ep.dist for ep in config._ensure_valid_entry_points() if ep.dist is not None] items = installable_items(packages, installed) core_constraints = { @@ -675,7 +674,7 @@ def addInstallable(self, installable): installable: Installable """ items = self.items() - installed = [ep.dist for ep in self.config().addon_entry_points()] + installed = [ep.dist for ep in self.config()._ensure_valid_entry_points()] new_ = installable_items([installable], filter(None, installed)) def match(item): diff --git a/orangecanvas/application/utils/addons.py b/orangecanvas/application/utils/addons.py index dbce16d6..ffc05516 100644 --- a/orangecanvas/application/utils/addons.py +++ b/orangecanvas/application/utils/addons.py @@ -369,7 +369,7 @@ def getname(item): # query pypi.org for installed add-ons that are not in the defaults # list - installed = [ep.dist for ep in config.addon_entry_points() + installed = [ep.dist for ep in config._ensure_valid_entry_points() if ep.dist is not None] missing = {dist.name.casefold() for dist in installed} - \ {name.casefold() for name in defaults_names} diff --git a/orangecanvas/config.py b/orangecanvas/config.py index 950b6592..43aee40c 100644 --- a/orangecanvas/config.py +++ b/orangecanvas/config.py @@ -25,7 +25,9 @@ from .gui.utils import windows_set_current_process_app_user_model_id from .gui.svgiconengine import SvgIconEngine from .utils.settings import Settings, config_slot -from .utils.pkgmeta import EntryPoint, Distribution, entry_points +from .utils.pkgmeta import ( + EntryPoint, Distribution, entry_points, ensure_valid_entry_point +) if typing.TYPE_CHECKING: import requests @@ -135,6 +137,9 @@ def addon_entry_points(self): # type: () -> Iterable[EntryPoint] return iter(()) + def _ensure_valid_entry_points(self): + return map(ensure_valid_entry_point, self.addon_entry_points()) + def addon_pypi_search_spec(self): return {} diff --git a/orangecanvas/utils/pkgmeta.py b/orangecanvas/utils/pkgmeta.py index 9bd2116a..9d4f488d 100644 --- a/orangecanvas/utils/pkgmeta.py +++ b/orangecanvas/utils/pkgmeta.py @@ -15,7 +15,8 @@ __all__ = [ "Distribution", "EntryPoint", "entry_points", "normalize_name", "trim", - "trim_leading_lines", "trim_trailing_lines", "get_distribution" + "trim_leading_lines", "trim_trailing_lines", "get_distribution", + "ensure_valid_entry_point" ] @@ -140,3 +141,26 @@ def get_distribution(name: str) -> Optional[Distribution]: return Distribution.from_name(name) except PackageNotFoundError: return None + + +def ensure_valid_entry_point(ep) -> EntryPoint: + if isinstance(ep, EntryPoint): + return ep + try: + # importlib_metadata.EntryPoint + group = ep.group + name = ep.name + value = ep.value + dist_name = ep.dist.name if ep.dist is not None else None + except AttributeError: + # pkg_resources.EntryPoint + group = "unknown" # does not record the group + name = ep.name + value = ep.module_name + if ep.attrs: + value += ":" + ".".join(ep.attrs) + dist_name = ep.dist.project_name if ep.dist is not None else None + dist = get_distribution(dist_name) if dist_name is not None else None + ep_ = EntryPoint(name, value, group) + vars(ep_).update(dist=dist) # EntryPoint is "immutable" + return ep_ diff --git a/orangecanvas/utils/tests/test_pkgmeta.py b/orangecanvas/utils/tests/test_pkgmeta.py new file mode 100644 index 00000000..bb1fb6a5 --- /dev/null +++ b/orangecanvas/utils/tests/test_pkgmeta.py @@ -0,0 +1,27 @@ +from types import SimpleNamespace +from unittest import TestCase + +from ..pkgmeta import ensure_valid_entry_point, EntryPoint + + +class TestPkgmeta(TestCase): + def test_ensure_valid_entry_point(self): + ep = ensure_valid_entry_point(EntryPoint("a", "b", "c")) + self.assertIsInstance(ep, EntryPoint) + self.assertEqual(ep.name, "a") + self.assertEqual(ep.value, "b") + self.assertEqual(ep.group, "c") + ep = ensure_valid_entry_point( + SimpleNamespace(name="a", value="b", group="c", dist=None) + ) + self.assertEqual(ep.name, "a") + self.assertEqual(ep.value, "b") + self.assertEqual(ep.group, "c") + + try: + from pkg_resources import EntryPoint as PREntryPoint + except ImportError: + return + ep = ensure_valid_entry_point(PREntryPoint("a", "b")) + self.assertEqual(ep.name, "a") + self.assertEqual(ep.value, "b")