make pytest_plugin_registered a historic hook

--HG--
branch : more_plugin
This commit is contained in:
holger krekel 2015-04-25 13:38:30 +02:00
parent e7a2e53108
commit 4e116ed503
5 changed files with 39 additions and 49 deletions

View File

@ -122,8 +122,8 @@ class PytestPluginManager(PluginManager):
if ret: if ret:
if not conftest: if not conftest:
self._globalplugins.append(plugin) self._globalplugins.append(plugin)
self.hook.pytest_plugin_registered(plugin=plugin, self.hook.pytest_plugin_registered.call_historic(
manager=self) kwargs=dict(plugin=plugin, manager=self))
return ret return ret
def unregister(self, plugin): def unregister(self, plugin):
@ -707,8 +707,8 @@ class Config(object):
def do_setns(dic): def do_setns(dic):
import pytest import pytest
setns(pytest, dic) setns(pytest, dic)
self.hook.pytest_namespace.call_historic({}, proc=do_setns) self.hook.pytest_namespace.call_historic(do_setns, {})
self.hook.pytest_addoption.call_historic(dict(parser=self._parser)) self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
def add_cleanup(self, func): def add_cleanup(self, func):
""" Add a function to be called when the config object gets out of """ Add a function to be called when the config object gets out of
@ -718,7 +718,7 @@ class Config(object):
def _do_configure(self): def _do_configure(self):
assert not self._configured assert not self._configured
self._configured = True 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): def _ensure_unconfigure(self):
if self._configured: if self._configured:
@ -840,7 +840,8 @@ class Config(object):
assert not hasattr(self, 'args'), ( assert not hasattr(self, 'args'), (
"can only parse cmdline args at most once per Config object") "can only parse cmdline args at most once per Config object")
self._origargs = args 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) self._preparse(args)
# XXX deprecated hook: # XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=args) self.hook.pytest_cmdline_preparse(config=self, args=args)

View File

@ -2,7 +2,7 @@
PluginManager, basic initialization and tracing. PluginManager, basic initialization and tracing.
""" """
import sys import sys
import inspect from inspect import isfunction, ismethod, isclass, formatargspec, getargspec
import py import py
py3 = sys.version_info > (3,0) py3 = sys.version_info > (3,0)
@ -267,7 +267,6 @@ class PluginManager(object):
def addhooks(self, module_or_class): def addhooks(self, module_or_class):
""" add new hook definitions from the given module_or_class using """ add new hook definitions from the given module_or_class using
the prefix/excludefunc with which the PluginManager was initialized. """ the prefix/excludefunc with which the PluginManager was initialized. """
isclass = int(inspect.isclass(module_or_class))
names = [] names = []
for name in dir(module_or_class): for name in dir(module_or_class):
if name.startswith(self._prefix): if name.startswith(self._prefix):
@ -394,17 +393,17 @@ def varnames(func, startindex=None):
return cache["_varnames"] return cache["_varnames"]
except KeyError: except KeyError:
pass pass
if inspect.isclass(func): if isclass(func):
try: try:
func = func.__init__ func = func.__init__
except AttributeError: except AttributeError:
return () return ()
startindex = 1 startindex = 1
else: else:
if not inspect.isfunction(func) and not inspect.ismethod(func): if not isfunction(func) and not ismethod(func):
func = getattr(func, '__call__', func) func = getattr(func, '__call__', func)
if startindex is None: if startindex is None:
startindex = int(inspect.ismethod(func)) startindex = int(ismethod(func))
rawcode = py.code.getrawcode(func) rawcode = py.code.getrawcode(func)
try: try:
@ -461,10 +460,9 @@ class HookCaller(object):
assert not self.has_spec() assert not self.has_spec()
self._specmodule_or_class = specmodule_or_class self._specmodule_or_class = specmodule_or_class
specfunc = getattr(specmodule_or_class, self.name) specfunc = getattr(specmodule_or_class, self.name)
self.argnames = ["__multicall__"] + list(varnames( argnames = varnames(specfunc, startindex=isclass(specmodule_or_class))
specfunc, startindex=inspect.isclass(specmodule_or_class) assert "self" not in argnames # sanity check
)) self.argnames = ["__multicall__"] + list(argnames)
assert "self" not in self.argnames # sanity check
self.firstresult = getattr(specfunc, 'firstresult', False) self.firstresult = getattr(specfunc, 'firstresult', False)
if hasattr(specfunc, "historic"): if hasattr(specfunc, "historic"):
self._call_history = [] self._call_history = []
@ -512,27 +510,26 @@ class HookCaller(object):
assert not self.is_historic() assert not self.is_historic()
return self._docall(self._nonwrappers + self._wrappers, kwargs) return self._docall(self._nonwrappers + self._wrappers, kwargs)
def callextra(self, methods, **kwargs): def call_extra(self, methods, kwargs):
assert not self.is_historic() assert not self.is_historic()
hc = self.clone() hc = self.clone()
for method in methods: for method in methods:
hc.add_method(method) hc.add_method(method)
return hc(**kwargs) return hc(**kwargs)
def _docall(self, methods, kwargs): def call_historic(self, proc=None, kwargs=None):
return MultiCall(methods, kwargs, firstresult=self.firstresult).execute() self._call_history.append((kwargs or {}, proc))
def call_historic(self, kwargs, proc=None):
self._call_history.append((kwargs, proc))
self._docall(self._nonwrappers + self._wrappers, kwargs) self._docall(self._nonwrappers + self._wrappers, kwargs)
def _apply_history(self, method): def _apply_history(self, method):
if self.is_historic(): if self.is_historic():
for kwargs, proc in self._call_history: for kwargs, proc in self._call_history:
args = [kwargs[argname] for argname in varnames(method)] res = self._docall([method], kwargs)
res = method(*args) if res and proc is not None:
if proc is not None: proc(res[0])
proc(res)
def _docall(self, methods, kwargs):
return MultiCall(methods, kwargs, firstresult=self.firstresult).execute()
class PluginValidationError(Exception): class PluginValidationError(Exception):
@ -542,5 +539,5 @@ class PluginValidationError(Exception):
def formatdef(func): def formatdef(func):
return "%s%s" % ( return "%s%s" % (
func.__name__, func.__name__,
inspect.formatargspec(*inspect.getargspec(func)) formatargspec(*getargspec(func))
) )

View File

@ -19,6 +19,11 @@ def pytest_namespace():
time. time.
""" """
@hookspec_opts(historic=True)
def pytest_plugin_registered(plugin, manager):
""" a new pytest plugin got registered. """
@hookspec_opts(historic=True) @hookspec_opts(historic=True)
def pytest_addoption(parser): def pytest_addoption(parser):
"""register argparse-style options and ini-style config values. """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 # error handling and internal debugging hooks
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
def pytest_plugin_registered(plugin, manager):
""" a new pytest plugin got registered. """
def pytest_internalerror(excrepr, excinfo): def pytest_internalerror(excrepr, excinfo):
""" called for internal errors. """ """ called for internal errors. """

View File

@ -381,7 +381,8 @@ class PyCollector(PyobjMixin, pytest.Collector):
if hasattr(cls, "pytest_generate_tests"): if hasattr(cls, "pytest_generate_tests"):
methods.append(cls().pytest_generate_tests) methods.append(cls().pytest_generate_tests)
if methods: if methods:
self.ihook.pytest_generate_tests.callextra(methods, metafunc=metafunc) self.ihook.pytest_generate_tests.call_extra(methods,
dict(metafunc=metafunc))
else: else:
self.ihook.pytest_generate_tests(metafunc=metafunc) self.ihook.pytest_generate_tests(metafunc=metafunc)
@ -1623,7 +1624,6 @@ class FixtureManager:
self.session = session self.session = session
self.config = session.config self.config = session.config
self._arg2fixturedefs = {} self._arg2fixturedefs = {}
self._seenplugins = set()
self._holderobjseen = set() self._holderobjseen = set()
self._arg2finish = {} self._arg2finish = {}
self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))] self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))]
@ -1648,11 +1648,7 @@ class FixtureManager:
node) node)
return FuncFixtureInfo(argnames, names_closure, arg2fixturedefs) 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): def pytest_plugin_registered(self, plugin):
if plugin in self._seenplugins:
return
nodeid = None nodeid = None
try: try:
p = py.path.local(plugin.__file__) p = py.path.local(plugin.__file__)
@ -1667,13 +1663,6 @@ class FixtureManager:
if p.sep != "/": if p.sep != "/":
nodeid = nodeid.replace(p.sep, "/") nodeid = nodeid.replace(p.sep, "/")
self.parsefactories(plugin, nodeid) 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): def _getautousenames(self, nodeid):
""" return a tuple of fixture names to be used. """ """ return a tuple of fixture names to be used. """

View File

@ -111,7 +111,7 @@ class TestPluginManager:
l.append(arg*10) l.append(arg*10)
pm.register(Plugin2()) pm.register(Plugin2())
assert l == [1, 10] 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] assert l == [1, 10, 120, 12]
def test_with_result_memorized(self, pm): def test_with_result_memorized(self, pm):
@ -122,7 +122,7 @@ class TestPluginManager:
pm.addhooks(Hooks) pm.addhooks(Hooks)
he_method1 = pm.hook.he_method1 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 = [] l = []
class Plugin: class Plugin:
def he_method1(self, arg): def he_method1(self, arg):
@ -140,7 +140,7 @@ class TestPluginManager:
def he_method1(arg): def he_method1(arg):
return arg * 10 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] assert l == [10]
@ -323,7 +323,8 @@ class TestPytestPluginInteractions:
""") """)
config = get_plugin_manager().config config = get_plugin_manager().config
pm = config.pluginmanager 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) config.pluginmanager._importconftest(conf)
#print(config.pluginmanager.getplugins()) #print(config.pluginmanager.getplugins())
res = config.hook.pytest_myhook(xyz=10) res = config.hook.pytest_myhook(xyz=10)
@ -399,10 +400,10 @@ class TestPytestPluginInteractions:
pytestpm = get_plugin_manager() # fully initialized with plugins pytestpm = get_plugin_manager() # fully initialized with plugins
saveindent = [] saveindent = []
class api1: class api1:
def pytest_plugin_registered(self, plugin): def pytest_plugin_registered(self):
saveindent.append(pytestpm.trace.root.indent) saveindent.append(pytestpm.trace.root.indent)
class api2: class api2:
def pytest_plugin_registered(self, plugin): def pytest_plugin_registered(self):
saveindent.append(pytestpm.trace.root.indent) saveindent.append(pytestpm.trace.root.indent)
raise ValueError() raise ValueError()
l = [] l = []
@ -412,7 +413,7 @@ class TestPytestPluginInteractions:
p = api1() p = api1()
pytestpm.register(p) pytestpm.register(p)
assert pytestpm.trace.root.indent == indent assert pytestpm.trace.root.indent == indent
assert len(l) == 2 assert len(l) >= 2
assert 'pytest_plugin_registered' in l[0] assert 'pytest_plugin_registered' in l[0]
assert 'finish' in l[1] assert 'finish' in l[1]