diff --git a/docs/conf.py b/docs/conf.py index 550071d7..e752b101 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import importlib_metadata +import pkg_resources extensions = [ @@ -20,13 +20,14 @@ # General information about the project. -project = "pluggy" +dist = pkg_resources.get_distribution("pluggy") +project = dist.project_name copyright = u"2016, Holger Krekel" author = "Holger Krekel" -release = importlib_metadata.version(project) +release = dist.version # The short X.Y version. -version = u".".join(release.split(".")[:2]) +version = u".".join(dist.version.split(".")[:2]) language = None diff --git a/pluggy/manager.py b/pluggy/manager.py index 4de1497b..351899a8 100644 --- a/pluggy/manager.py +++ b/pluggy/manager.py @@ -3,8 +3,6 @@ from .hooks import HookImpl, _HookRelay, _HookCaller, normalize_hookimpl_opts import warnings -import importlib_metadata - def _warn_for_function(warning, function): warnings.warn_explicit( @@ -27,23 +25,6 @@ def __init__(self, plugin, message): super(Exception, self).__init__(message) -class DistFacade(object): - """Emulate a pkg_resources Distribution""" - - def __init__(self, dist): - self._dist = dist - - @property - def project_name(self): - return self.metadata["name"] - - def __getattr__(self, attr, default=None): - return getattr(self._dist, attr, default) - - def __dir__(self): - return sorted(dir(self._dist) + ["_dist", "project_name"]) - - class PluginManager(object): """ Core Pluginmanager class which manages registration of plugin objects and 1:N hook calling. @@ -278,18 +259,29 @@ def load_setuptools_entrypoints(self, group, name=None): :rtype: int :return: return the number of loaded plugins by this call. """ + from pkg_resources import ( + iter_entry_points, + DistributionNotFound, + VersionConflict, + ) + count = 0 - for dist in importlib_metadata.distributions(): - for ep in dist.entry_points: - if ep.group != group or (name is not None and ep.name != name): - continue - # is the plugin registered or blocked? - if self.get_plugin(ep.name) or self.is_blocked(ep.name): - continue + for ep in iter_entry_points(group, name=name): + # is the plugin registered or blocked? + if self.get_plugin(ep.name) or self.is_blocked(ep.name): + continue + try: plugin = ep.load() - self.register(plugin, name=ep.name) - self._plugin_distinfo.append((plugin, DistFacade(dist))) - count += 1 + except DistributionNotFound: + continue + except VersionConflict as e: + raise PluginValidationError( + plugin=None, + message="Plugin %r could not be loaded: %s!" % (ep.name, e), + ) + self.register(plugin, name=ep.name) + self._plugin_distinfo.append((plugin, ep.dist)) + count += 1 return count def list_plugin_distinfo(self): diff --git a/setup.py b/setup.py index 866d194e..d60e5fd9 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,6 @@ def main(): author_email="holger@merlinux.eu", url="https://github.com/pytest-dev/pluggy", python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", - install_requires=["importlib-metadata>=0.9"], extras_require={"dev": ["pre-commit", "tox"]}, classifiers=classifiers, packages=["pluggy"], diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index b226c413..20ede3c3 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -3,7 +3,7 @@ """ import pytest import types -import importlib_metadata +import sys from pluggy import ( PluginManager, PluginValidationError, @@ -447,40 +447,64 @@ def example_hook(): def test_load_setuptools_instantiation(monkeypatch, pm): - class EntryPoint(object): - name = "myname" - group = "hello" - value = "myname:foo" + pkg_resources = pytest.importorskip("pkg_resources") - def load(self): - class PseudoPlugin(object): - x = 42 + def my_iter(group, name=None): + assert group == "hello" - return PseudoPlugin() + class EntryPoint(object): + name = "myname" + dist = None - class Distribution(object): - entry_points = (EntryPoint(),) + def load(self): + class PseudoPlugin(object): + x = 42 - dist = Distribution() + return PseudoPlugin() - def my_distributions(): - return (dist,) + return iter([EntryPoint()]) - monkeypatch.setattr(importlib_metadata, "distributions", my_distributions) + monkeypatch.setattr(pkg_resources, "iter_entry_points", my_iter) num = pm.load_setuptools_entrypoints("hello") assert num == 1 plugin = pm.get_plugin("myname") assert plugin.x == 42 - ret = pm.list_plugin_distinfo() - # poor man's `assert ret == [(plugin, mock.ANY)]` - assert len(ret) == 1 - assert len(ret[0]) == 2 - assert ret[0][0] == plugin - assert ret[0][1]._dist == dist + assert pm.list_plugin_distinfo() == [(plugin, None)] num = pm.load_setuptools_entrypoints("hello") assert num == 0 # no plugin loaded by this call +def test_load_setuptools_version_conflict(monkeypatch, pm): + """Check that we properly handle a VersionConflict problem when loading entry points""" + pkg_resources = pytest.importorskip("pkg_resources") + + def my_iter(group, name=None): + assert group == "hello" + + class EntryPoint(object): + name = "myname" + dist = None + + def load(self): + raise pkg_resources.VersionConflict("Some conflict") + + return iter([EntryPoint()]) + + monkeypatch.setattr(pkg_resources, "iter_entry_points", my_iter) + with pytest.raises( + PluginValidationError, + match="Plugin 'myname' could not be loaded: Some conflict!", + ): + pm.load_setuptools_entrypoints("hello") + + +def test_load_setuptools_not_installed(monkeypatch, pm): + monkeypatch.setitem(sys.modules, "pkg_resources", types.ModuleType("pkg_resources")) + + with pytest.raises(ImportError): + pm.load_setuptools_entrypoints("qwe") + + def test_add_tracefuncs(he_pm): out = []