diff --git a/_pytest/config.py b/_pytest/config.py index 860f34e53..d75cb6600 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -122,8 +122,8 @@ class PytestPluginManager(PluginManager): if ret: if not conftest: self._globalplugins.append(plugin) - self.hook.pytest_plugin_registered(plugin=plugin, - manager=self) + self.hook.pytest_plugin_registered.call_historic( + kwargs=dict(plugin=plugin, manager=self)) return ret def unregister(self, plugin): @@ -707,8 +707,8 @@ class Config(object): def do_setns(dic): import pytest setns(pytest, dic) - self.hook.pytest_namespace.call_historic({}, proc=do_setns) - self.hook.pytest_addoption.call_historic(dict(parser=self._parser)) + self.hook.pytest_namespace.call_historic(do_setns, {}) + self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser)) def add_cleanup(self, func): """ Add a function to be called when the config object gets out of @@ -718,7 +718,7 @@ class Config(object): def _do_configure(self): assert not self._configured self._configured = True - self.hook.pytest_configure.call_historic(dict(config=self)) + self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) def _ensure_unconfigure(self): if self._configured: @@ -840,7 +840,8 @@ class Config(object): assert not hasattr(self, 'args'), ( "can only parse cmdline args at most once per Config object") self._origargs = args - self.hook.pytest_addhooks.call_historic(dict(pluginmanager=self.pluginmanager)) + self.hook.pytest_addhooks.call_historic( + kwargs=dict(pluginmanager=self.pluginmanager)) self._preparse(args) # XXX deprecated hook: self.hook.pytest_cmdline_preparse(config=self, args=args) diff --git a/_pytest/core.py b/_pytest/core.py index b394913cd..af42ea914 100644 --- a/_pytest/core.py +++ b/_pytest/core.py @@ -2,7 +2,7 @@ PluginManager, basic initialization and tracing. """ import sys -import inspect +from inspect import isfunction, ismethod, isclass, formatargspec, getargspec import py py3 = sys.version_info > (3,0) @@ -267,7 +267,6 @@ class PluginManager(object): def addhooks(self, module_or_class): """ add new hook definitions from the given module_or_class using the prefix/excludefunc with which the PluginManager was initialized. """ - isclass = int(inspect.isclass(module_or_class)) names = [] for name in dir(module_or_class): if name.startswith(self._prefix): @@ -394,17 +393,17 @@ def varnames(func, startindex=None): return cache["_varnames"] except KeyError: pass - if inspect.isclass(func): + if isclass(func): try: func = func.__init__ except AttributeError: return () startindex = 1 else: - if not inspect.isfunction(func) and not inspect.ismethod(func): + if not isfunction(func) and not ismethod(func): func = getattr(func, '__call__', func) if startindex is None: - startindex = int(inspect.ismethod(func)) + startindex = int(ismethod(func)) rawcode = py.code.getrawcode(func) try: @@ -461,10 +460,9 @@ class HookCaller(object): assert not self.has_spec() self._specmodule_or_class = specmodule_or_class specfunc = getattr(specmodule_or_class, self.name) - self.argnames = ["__multicall__"] + list(varnames( - specfunc, startindex=inspect.isclass(specmodule_or_class) - )) - assert "self" not in self.argnames # sanity check + argnames = varnames(specfunc, startindex=isclass(specmodule_or_class)) + assert "self" not in argnames # sanity check + self.argnames = ["__multicall__"] + list(argnames) self.firstresult = getattr(specfunc, 'firstresult', False) if hasattr(specfunc, "historic"): self._call_history = [] @@ -512,27 +510,26 @@ class HookCaller(object): assert not self.is_historic() return self._docall(self._nonwrappers + self._wrappers, kwargs) - def callextra(self, methods, **kwargs): + def call_extra(self, methods, kwargs): assert not self.is_historic() hc = self.clone() for method in methods: hc.add_method(method) return hc(**kwargs) - def _docall(self, methods, kwargs): - return MultiCall(methods, kwargs, firstresult=self.firstresult).execute() - - def call_historic(self, kwargs, proc=None): - self._call_history.append((kwargs, proc)) + def call_historic(self, proc=None, kwargs=None): + self._call_history.append((kwargs or {}, proc)) self._docall(self._nonwrappers + self._wrappers, kwargs) def _apply_history(self, method): if self.is_historic(): for kwargs, proc in self._call_history: - args = [kwargs[argname] for argname in varnames(method)] - res = method(*args) - if proc is not None: - proc(res) + res = self._docall([method], kwargs) + if res and proc is not None: + proc(res[0]) + + def _docall(self, methods, kwargs): + return MultiCall(methods, kwargs, firstresult=self.firstresult).execute() class PluginValidationError(Exception): @@ -542,5 +539,5 @@ class PluginValidationError(Exception): def formatdef(func): return "%s%s" % ( func.__name__, - inspect.formatargspec(*inspect.getargspec(func)) + formatargspec(*getargspec(func)) ) diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index e230920dd..cf8947ada 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -19,6 +19,11 @@ def pytest_namespace(): time. """ +@hookspec_opts(historic=True) +def pytest_plugin_registered(plugin, manager): + """ a new pytest plugin got registered. """ + + @hookspec_opts(historic=True) def pytest_addoption(parser): """register argparse-style options and ini-style config values. @@ -259,9 +264,6 @@ def pytest_doctest_prepare_content(content): # error handling and internal debugging hooks # ------------------------------------------------------------------------- -def pytest_plugin_registered(plugin, manager): - """ a new pytest plugin got registered. """ - def pytest_internalerror(excrepr, excinfo): """ called for internal errors. """ diff --git a/_pytest/python.py b/_pytest/python.py index bc64d6fa3..e849ca6fe 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -381,7 +381,8 @@ class PyCollector(PyobjMixin, pytest.Collector): if hasattr(cls, "pytest_generate_tests"): methods.append(cls().pytest_generate_tests) if methods: - self.ihook.pytest_generate_tests.callextra(methods, metafunc=metafunc) + self.ihook.pytest_generate_tests.call_extra(methods, + dict(metafunc=metafunc)) else: self.ihook.pytest_generate_tests(metafunc=metafunc) @@ -1623,7 +1624,6 @@ class FixtureManager: self.session = session self.config = session.config self._arg2fixturedefs = {} - self._seenplugins = set() self._holderobjseen = set() self._arg2finish = {} self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))] @@ -1648,11 +1648,7 @@ class FixtureManager: node) return FuncFixtureInfo(argnames, names_closure, arg2fixturedefs) - ### XXX this hook should be called for historic events like pytest_configure - ### so that we don't have to do the below pytest_configure hook def pytest_plugin_registered(self, plugin): - if plugin in self._seenplugins: - return nodeid = None try: p = py.path.local(plugin.__file__) @@ -1667,13 +1663,6 @@ class FixtureManager: if p.sep != "/": nodeid = nodeid.replace(p.sep, "/") self.parsefactories(plugin, nodeid) - self._seenplugins.add(plugin) - - @pytest.hookimpl_opts(tryfirst=True) - def pytest_configure(self, config): - plugins = config.pluginmanager.getplugins() - for plugin in plugins: - self.pytest_plugin_registered(plugin) def _getautousenames(self, nodeid): """ return a tuple of fixture names to be used. """ diff --git a/testing/test_core.py b/testing/test_core.py index d3fa0968d..f9a1f0ea5 100644 --- a/testing/test_core.py +++ b/testing/test_core.py @@ -111,7 +111,7 @@ class TestPluginManager: l.append(arg*10) pm.register(Plugin2()) assert l == [1, 10] - pm.hook.he_method1.call_historic(dict(arg=12)) + pm.hook.he_method1.call_historic(kwargs=dict(arg=12)) assert l == [1, 10, 120, 12] def test_with_result_memorized(self, pm): @@ -122,7 +122,7 @@ class TestPluginManager: pm.addhooks(Hooks) he_method1 = pm.hook.he_method1 - he_method1.call_historic(proc=lambda res: l.append(res), kwargs=dict(arg=1)) + he_method1.call_historic(lambda res: l.append(res), dict(arg=1)) l = [] class Plugin: def he_method1(self, arg): @@ -140,7 +140,7 @@ class TestPluginManager: def he_method1(arg): return arg * 10 - l = pm.hook.he_method1.callextra([he_method1], arg=1) + l = pm.hook.he_method1.call_extra([he_method1], dict(arg=1)) assert l == [10] @@ -323,7 +323,8 @@ class TestPytestPluginInteractions: """) config = get_plugin_manager().config pm = config.pluginmanager - pm.hook.pytest_addhooks.call_historic(dict(pluginmanager=config.pluginmanager)) + pm.hook.pytest_addhooks.call_historic( + kwargs=dict(pluginmanager=config.pluginmanager)) config.pluginmanager._importconftest(conf) #print(config.pluginmanager.getplugins()) res = config.hook.pytest_myhook(xyz=10) @@ -399,10 +400,10 @@ class TestPytestPluginInteractions: pytestpm = get_plugin_manager() # fully initialized with plugins saveindent = [] class api1: - def pytest_plugin_registered(self, plugin): + def pytest_plugin_registered(self): saveindent.append(pytestpm.trace.root.indent) class api2: - def pytest_plugin_registered(self, plugin): + def pytest_plugin_registered(self): saveindent.append(pytestpm.trace.root.indent) raise ValueError() l = [] @@ -412,7 +413,7 @@ class TestPytestPluginInteractions: p = api1() pytestpm.register(p) assert pytestpm.trace.root.indent == indent - assert len(l) == 2 + assert len(l) >= 2 assert 'pytest_plugin_registered' in l[0] assert 'finish' in l[1]