diff --git a/appveyor.yml b/appveyor.yml index 61da86e1..176c56a2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,7 +31,7 @@ install: build: false # Not a C# project, build stuff at the test step instead. test_script: - - C:\Python35\python -m tox + - C:\Python35\Scripts\tox # We don't deploy anything on tags with AppVeyor, we use Travis instead, so we # might as well save resources diff --git a/docs/conf.py b/docs/conf.py index e752b101..550071d7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import pkg_resources +import importlib_metadata extensions = [ @@ -20,14 +20,13 @@ # General information about the project. -dist = pkg_resources.get_distribution("pluggy") -project = dist.project_name +project = "pluggy" copyright = u"2016, Holger Krekel" author = "Holger Krekel" -release = dist.version +release = importlib_metadata.version(project) # The short X.Y version. -version = u".".join(dist.version.split(".")[:2]) +version = u".".join(release.split(".")[:2]) language = None diff --git a/pluggy/manager.py b/pluggy/manager.py index 351899a8..4de1497b 100644 --- a/pluggy/manager.py +++ b/pluggy/manager.py @@ -3,6 +3,8 @@ from .hooks import HookImpl, _HookRelay, _HookCaller, normalize_hookimpl_opts import warnings +import importlib_metadata + def _warn_for_function(warning, function): warnings.warn_explicit( @@ -25,6 +27,23 @@ 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. @@ -259,29 +278,18 @@ 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 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: + 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 plugin = ep.load() - 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 + self.register(plugin, name=ep.name) + self._plugin_distinfo.append((plugin, DistFacade(dist))) + count += 1 return count def list_plugin_distinfo(self): diff --git a/setup.py b/setup.py index d60e5fd9..866d194e 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ 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 20ede3c3..b226c413 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -3,7 +3,7 @@ """ import pytest import types -import sys +import importlib_metadata from pluggy import ( PluginManager, PluginValidationError, @@ -447,64 +447,40 @@ def example_hook(): def test_load_setuptools_instantiation(monkeypatch, pm): - pkg_resources = pytest.importorskip("pkg_resources") + class EntryPoint(object): + name = "myname" + group = "hello" + value = "myname:foo" - def my_iter(group, name=None): - assert group == "hello" + def load(self): + class PseudoPlugin(object): + x = 42 - class EntryPoint(object): - name = "myname" - dist = None + return PseudoPlugin() - def load(self): - class PseudoPlugin(object): - x = 42 + class Distribution(object): + entry_points = (EntryPoint(),) - return PseudoPlugin() + dist = Distribution() - return iter([EntryPoint()]) + def my_distributions(): + return (dist,) - monkeypatch.setattr(pkg_resources, "iter_entry_points", my_iter) + monkeypatch.setattr(importlib_metadata, "distributions", my_distributions) num = pm.load_setuptools_entrypoints("hello") assert num == 1 plugin = pm.get_plugin("myname") assert plugin.x == 42 - assert pm.list_plugin_distinfo() == [(plugin, None)] + 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 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 = []