diff --git a/_pytest/config.py b/_pytest/config.py index bb0d07962..581e14e94 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -9,7 +9,7 @@ import py # DON't import pytest here because it causes import cycle troubles import sys, os from _pytest import hookspec # the extension point definitions -from _pytest.core import PluginManager +from _pytest.core import PluginManager, hookimpl_opts # pytest startup # @@ -98,7 +98,6 @@ class PytestPluginManager(PluginManager): super(PytestPluginManager, self).__init__(prefix="pytest_", excludefunc=exclude_pytest_names) self._warnings = [] - self._plugin_distinfo = [] self._conftest_plugins = set() # state related to local conftest plugins @@ -126,16 +125,10 @@ class PytestPluginManager(PluginManager): return ret def getplugin(self, name): - # deprecated naming + # support deprecated naming because plugins (xdist e.g.) use it return self.get_plugin(name) def pytest_configure(self, config): - config.addinivalue_line("markers", - "tryfirst: mark a hook implementation function such that the " - "plugin machinery will try to call it first/as early as possible.") - config.addinivalue_line("markers", - "trylast: mark a hook implementation function such that the " - "plugin machinery will try to call it last/as late as possible.") for warning in self._warnings: config.warn(code="I1", message=warning) @@ -236,21 +229,6 @@ class PytestPluginManager(PluginManager): # # - def consider_setuptools_entrypoints(self): - try: - from pkg_resources import iter_entry_points, DistributionNotFound - except ImportError: - return # XXX issue a warning - for ep in iter_entry_points('pytest11'): - if self.get_plugin(ep.name) or ep.name in self._name2plugin: - continue - try: - plugin = ep.load() - except DistributionNotFound: - continue - self.register(plugin, name=ep.name) - self._plugin_distinfo.append((ep.dist, plugin)) - def consider_preparse(self, args): for opt1,opt2 in zip(args, args[1:]): if opt1 == "-p": @@ -679,6 +657,7 @@ class Notset: notset = Notset() FILE_OR_DIR = 'file_or_dir' + class Config(object): """ access to configuration values, pluginmanager and plugin hooks. """ @@ -779,9 +758,9 @@ class Config(object): if not hasattr(self.option, opt.dest): setattr(self.option, opt.dest, opt.default) + @hookimpl_opts(trylast=True) def pytest_load_initial_conftests(self, early_config): self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) - pytest_load_initial_conftests.trylast = True def _initini(self, args): parsed_args = self._parser.parse_known_args(args) @@ -798,7 +777,7 @@ class Config(object): args[:] = self.getini("addopts") + args self._checkversion() self.pluginmanager.consider_preparse(args) - self.pluginmanager.consider_setuptools_entrypoints() + self.pluginmanager.load_setuptools_entrypoints("pytest11") self.pluginmanager.consider_env() self.known_args_namespace = ns = self._parser.parse_known_args(args) try: diff --git a/_pytest/core.py b/_pytest/core.py index 82b9416ed..9bd9819d0 100644 --- a/_pytest/core.py +++ b/_pytest/core.py @@ -212,6 +212,7 @@ class PluginManager(object): self._excludefunc = excludefunc self._name2plugin = {} self._plugin2hookcallers = {} + self._plugin_distinfo = [] self.trace = TagTracer().get("pluginmanage") self.hook = HookRelay(self.trace.root.get("hook")) self._inner_hookexec = lambda hook, methods, kwargs: \ @@ -379,6 +380,25 @@ class PluginManager(object): raise PluginValidationError( "unknown hook %r in plugin %r" %(name, plugin)) + def load_setuptools_entrypoints(self, entrypoint_name): + """ Load modules from querying the specified entrypoint name. + Return None if setuptools was not operable, otherwise + the number of loaded plugins. """ + try: + from pkg_resources import iter_entry_points, DistributionNotFound + except ImportError: + return # XXX issue a warning + for ep in iter_entry_points(entrypoint_name): + if self.get_plugin(ep.name) or ep.name in self._name2plugin: + continue + try: + plugin = ep.load() + except DistributionNotFound: + continue + self.register(plugin, name=ep.name) + self._plugin_distinfo.append((ep.dist, plugin)) + return len(self._plugin_distinfo) + class MultiCall: """ execute a call into multiple python functions/methods. """ diff --git a/testing/test_core.py b/testing/test_core.py index 344b1047a..1003d6e26 100644 --- a/testing/test_core.py +++ b/testing/test_core.py @@ -383,6 +383,32 @@ class TestAddMethodOrdering: results = pm.hook.he_myhook(arg1=17) assert results == 18 + def test_load_setuptools_instantiation(self, monkeypatch, pm): + pkg_resources = pytest.importorskip("pkg_resources") + def my_iter(name): + assert name == "hello" + class EntryPoint: + name = "myname" + dist = None + def load(self): + class PseudoPlugin: + x = 42 + return PseudoPlugin() + return iter([EntryPoint()]) + + 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 + assert pm._plugin_distinfo == [(None, plugin)] + + def test_load_setuptools_not_installed(self, monkeypatch, pm): + monkeypatch.setitem(py.std.sys.modules, 'pkg_resources', + py.std.types.ModuleType("pkg_resources")) + assert pm.load_setuptools_entrypoints("qwe") is None + # ok, we did not explode + class TestPytestPluginInteractions: @@ -932,30 +958,6 @@ class TestPytestPluginManager: l3 = len(pytestpm.get_plugins()) assert l2 == l3 - def test_consider_setuptools_instantiation(self, monkeypatch, pytestpm): - pkg_resources = pytest.importorskip("pkg_resources") - def my_iter(name): - assert name == "pytest11" - class EntryPoint: - name = "pytest_mytestplugin" - dist = None - def load(self): - class PseudoPlugin: - x = 42 - return PseudoPlugin() - return iter([EntryPoint()]) - - monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter) - pytestpm.consider_setuptools_entrypoints() - plugin = pytestpm.get_plugin("pytest_mytestplugin") - assert plugin.x == 42 - - def test_consider_setuptools_not_installed(self, monkeypatch, pytestpm): - monkeypatch.setitem(py.std.sys.modules, 'pkg_resources', - py.std.types.ModuleType("pkg_resources")) - pytestpm.consider_setuptools_entrypoints() - # ok, we did not explode - def test_pluginmanager_ENV_startup(self, testdir, monkeypatch): testdir.makepyfile(pytest_x500="#") p = testdir.makepyfile("""