refine internal management of plugins and conftest files
This commit is contained in:
parent
c7a45d6eaf
commit
ea5fb0c153
|
@ -682,11 +682,8 @@ class Config(object):
|
||||||
setattr(self.option, opt.dest, opt.default)
|
setattr(self.option, opt.dest, opt.default)
|
||||||
|
|
||||||
def _getmatchingplugins(self, fspath):
|
def _getmatchingplugins(self, fspath):
|
||||||
allconftests = self._conftest._conftestpath2mod.values()
|
return self.pluginmanager._plugins + \
|
||||||
plugins = [x for x in self.pluginmanager.getplugins()
|
self._conftest.getconftestmodules(fspath)
|
||||||
if x not in allconftests]
|
|
||||||
plugins += self._conftest.getconftestmodules(fspath)
|
|
||||||
return plugins
|
|
||||||
|
|
||||||
def pytest_load_initial_conftests(self, early_config):
|
def pytest_load_initial_conftests(self, early_config):
|
||||||
self._conftest.setinitial(early_config.known_args_namespace)
|
self._conftest.setinitial(early_config.known_args_namespace)
|
||||||
|
|
|
@ -72,6 +72,7 @@ class PluginManager(object):
|
||||||
self._name2plugin = {}
|
self._name2plugin = {}
|
||||||
self._listattrcache = {}
|
self._listattrcache = {}
|
||||||
self._plugins = []
|
self._plugins = []
|
||||||
|
self._conftestplugins = []
|
||||||
self._warnings = []
|
self._warnings = []
|
||||||
self.trace = TagTracer().get("pluginmanage")
|
self.trace = TagTracer().get("pluginmanage")
|
||||||
self._plugin_distinfo = []
|
self._plugin_distinfo = []
|
||||||
|
@ -86,7 +87,7 @@ class PluginManager(object):
|
||||||
assert not hasattr(self, "_registercallback")
|
assert not hasattr(self, "_registercallback")
|
||||||
self._registercallback = callback
|
self._registercallback = callback
|
||||||
|
|
||||||
def register(self, plugin, name=None, prepend=False):
|
def register(self, plugin, name=None, prepend=False, conftest=False):
|
||||||
if self._name2plugin.get(name, None) == -1:
|
if self._name2plugin.get(name, None) == -1:
|
||||||
return
|
return
|
||||||
name = name or getattr(plugin, '__name__', str(id(plugin)))
|
name = name or getattr(plugin, '__name__', str(id(plugin)))
|
||||||
|
@ -98,16 +99,22 @@ class PluginManager(object):
|
||||||
reg = getattr(self, "_registercallback", None)
|
reg = getattr(self, "_registercallback", None)
|
||||||
if reg is not None:
|
if reg is not None:
|
||||||
reg(plugin, name)
|
reg(plugin, name)
|
||||||
if not prepend:
|
if conftest:
|
||||||
self._plugins.append(plugin)
|
self._conftestplugins.append(plugin)
|
||||||
else:
|
else:
|
||||||
self._plugins.insert(0, plugin)
|
if not prepend:
|
||||||
|
self._plugins.append(plugin)
|
||||||
|
else:
|
||||||
|
self._plugins.insert(0, plugin)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def unregister(self, plugin=None, name=None):
|
def unregister(self, plugin=None, name=None):
|
||||||
if plugin is None:
|
if plugin is None:
|
||||||
plugin = self.getplugin(name=name)
|
plugin = self.getplugin(name=name)
|
||||||
self._plugins.remove(plugin)
|
try:
|
||||||
|
self._plugins.remove(plugin)
|
||||||
|
except KeyError:
|
||||||
|
self._conftestplugins.remove(plugin)
|
||||||
for name, value in list(self._name2plugin.items()):
|
for name, value in list(self._name2plugin.items()):
|
||||||
if value == plugin:
|
if value == plugin:
|
||||||
del self._name2plugin[name]
|
del self._name2plugin[name]
|
||||||
|
@ -119,7 +126,7 @@ class PluginManager(object):
|
||||||
while self._shutdown:
|
while self._shutdown:
|
||||||
func = self._shutdown.pop()
|
func = self._shutdown.pop()
|
||||||
func()
|
func()
|
||||||
self._plugins = []
|
self._plugins = self._conftestplugins = []
|
||||||
self._name2plugin.clear()
|
self._name2plugin.clear()
|
||||||
self._listattrcache.clear()
|
self._listattrcache.clear()
|
||||||
|
|
||||||
|
@ -134,7 +141,7 @@ class PluginManager(object):
|
||||||
self.hook._addhooks(spec, prefix=prefix)
|
self.hook._addhooks(spec, prefix=prefix)
|
||||||
|
|
||||||
def getplugins(self):
|
def getplugins(self):
|
||||||
return list(self._plugins)
|
return self._plugins + self._conftestplugins
|
||||||
|
|
||||||
def skipifmissing(self, name):
|
def skipifmissing(self, name):
|
||||||
if not self.hasplugin(name):
|
if not self.hasplugin(name):
|
||||||
|
@ -198,7 +205,8 @@ class PluginManager(object):
|
||||||
self.import_plugin(arg)
|
self.import_plugin(arg)
|
||||||
|
|
||||||
def consider_conftest(self, conftestmodule):
|
def consider_conftest(self, conftestmodule):
|
||||||
if self.register(conftestmodule, name=conftestmodule.__file__):
|
if self.register(conftestmodule, name=conftestmodule.__file__,
|
||||||
|
conftest=True):
|
||||||
self.consider_module(conftestmodule)
|
self.consider_module(conftestmodule)
|
||||||
|
|
||||||
def consider_module(self, mod):
|
def consider_module(self, mod):
|
||||||
|
@ -233,12 +241,7 @@ class PluginManager(object):
|
||||||
|
|
||||||
def listattr(self, attrname, plugins=None):
|
def listattr(self, attrname, plugins=None):
|
||||||
if plugins is None:
|
if plugins is None:
|
||||||
plugins = self._plugins
|
plugins = self._plugins + self._conftestplugins
|
||||||
key = (attrname,) + tuple(plugins)
|
|
||||||
try:
|
|
||||||
return list(self._listattrcache[key])
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
l = []
|
l = []
|
||||||
last = []
|
last = []
|
||||||
wrappers = []
|
wrappers = []
|
||||||
|
@ -257,7 +260,7 @@ class PluginManager(object):
|
||||||
l.append(meth)
|
l.append(meth)
|
||||||
l.extend(last)
|
l.extend(last)
|
||||||
l.extend(wrappers)
|
l.extend(wrappers)
|
||||||
self._listattrcache[key] = list(l)
|
#self._listattrcache[key] = list(l)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
def call_plugin(self, plugin, methname, kwargs):
|
def call_plugin(self, plugin, methname, kwargs):
|
||||||
|
@ -397,6 +400,29 @@ class HookRelay:
|
||||||
raise ValueError("did not find new %r hooks in %r" %(
|
raise ValueError("did not find new %r hooks in %r" %(
|
||||||
prefix, hookspecs,))
|
prefix, hookspecs,))
|
||||||
|
|
||||||
|
def _getcaller(self, name, plugins):
|
||||||
|
caller = getattr(self, name)
|
||||||
|
methods = self._pm.listattr(name, plugins=plugins)
|
||||||
|
return CachedHookCaller(caller, methods)
|
||||||
|
|
||||||
|
|
||||||
|
class CachedHookCaller:
|
||||||
|
def __init__(self, hookmethod, methods):
|
||||||
|
self.hookmethod = hookmethod
|
||||||
|
self.methods = methods
|
||||||
|
|
||||||
|
def __call__(self, **kwargs):
|
||||||
|
return self.hookmethod._docall(self.methods, kwargs)
|
||||||
|
|
||||||
|
def callextra(self, methods, **kwargs):
|
||||||
|
# XXX in theory we should respect "tryfirst/trylast" if set
|
||||||
|
# on the added methods but we currently only use it for
|
||||||
|
# pytest_generate_tests and it doesn't make sense there i'd think
|
||||||
|
all = self.methods
|
||||||
|
if methods:
|
||||||
|
all = all + methods
|
||||||
|
return self.hookmethod._docall(all, kwargs)
|
||||||
|
|
||||||
|
|
||||||
class HookCaller:
|
class HookCaller:
|
||||||
def __init__(self, hookrelay, name, firstresult):
|
def __init__(self, hookrelay, name, firstresult):
|
||||||
|
@ -412,10 +438,6 @@ class HookCaller:
|
||||||
methods = self.hookrelay._pm.listattr(self.name)
|
methods = self.hookrelay._pm.listattr(self.name)
|
||||||
return self._docall(methods, kwargs)
|
return self._docall(methods, kwargs)
|
||||||
|
|
||||||
def pcall(self, plugins, **kwargs):
|
|
||||||
methods = self.hookrelay._pm.listattr(self.name, plugins=plugins)
|
|
||||||
return self._docall(methods, kwargs)
|
|
||||||
|
|
||||||
def _docall(self, methods, kwargs):
|
def _docall(self, methods, kwargs):
|
||||||
self.trace(self.name, kwargs)
|
self.trace(self.name, kwargs)
|
||||||
self.trace.root.indent += 1
|
self.trace.root.indent += 1
|
||||||
|
|
|
@ -153,19 +153,39 @@ def pytest_ignore_collect(path, config):
|
||||||
ignore_paths.extend([py.path.local(x) for x in excludeopt])
|
ignore_paths.extend([py.path.local(x) for x in excludeopt])
|
||||||
return path in ignore_paths
|
return path in ignore_paths
|
||||||
|
|
||||||
class HookProxy(object):
|
class FSHookProxy(object):
|
||||||
def __init__(self, fspath, config):
|
def __init__(self, fspath, config):
|
||||||
self.fspath = fspath
|
self.fspath = fspath
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
config = object.__getattribute__(self, "config")
|
plugins = self.config._getmatchingplugins(self.fspath)
|
||||||
hookmethod = getattr(config.hook, name)
|
x = self.config.hook._getcaller(name, plugins)
|
||||||
|
self.__dict__[name] = x
|
||||||
|
return x
|
||||||
|
|
||||||
def call_matching_hooks(**kwargs):
|
hookmethod = getattr(self.config.hook, name)
|
||||||
plugins = self.config._getmatchingplugins(self.fspath)
|
methods = self.config.pluginmanager.listattr(name, plugins=plugins)
|
||||||
return hookmethod.pcall(plugins, **kwargs)
|
self.__dict__[name] = x = HookCaller(hookmethod, methods)
|
||||||
return call_matching_hooks
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
class HookCaller:
|
||||||
|
def __init__(self, hookmethod, methods):
|
||||||
|
self.hookmethod = hookmethod
|
||||||
|
self.methods = methods
|
||||||
|
|
||||||
|
def __call__(self, **kwargs):
|
||||||
|
return self.hookmethod._docall(self.methods, kwargs)
|
||||||
|
|
||||||
|
def callextra(self, methods, **kwargs):
|
||||||
|
# XXX in theory we should respect "tryfirst/trylast" if set
|
||||||
|
# on the added methods but we currently only use it for
|
||||||
|
# pytest_generate_tests and it doesn't make sense there i'd think
|
||||||
|
all = self.methods
|
||||||
|
if methods:
|
||||||
|
all = all + methods
|
||||||
|
return self.hookmethod._docall(all, kwargs)
|
||||||
|
|
||||||
def compatproperty(name):
|
def compatproperty(name):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
|
@ -520,6 +540,7 @@ class Session(FSCollector):
|
||||||
self.trace = config.trace.root.get("collection")
|
self.trace = config.trace.root.get("collection")
|
||||||
self._norecursepatterns = config.getini("norecursedirs")
|
self._norecursepatterns = config.getini("norecursedirs")
|
||||||
self.startdir = py.path.local()
|
self.startdir = py.path.local()
|
||||||
|
self._fs2hookproxy = {}
|
||||||
|
|
||||||
def pytest_collectstart(self):
|
def pytest_collectstart(self):
|
||||||
if self.shouldstop:
|
if self.shouldstop:
|
||||||
|
@ -538,7 +559,11 @@ class Session(FSCollector):
|
||||||
return path in self._initialpaths
|
return path in self._initialpaths
|
||||||
|
|
||||||
def gethookproxy(self, fspath):
|
def gethookproxy(self, fspath):
|
||||||
return HookProxy(fspath, self.config)
|
try:
|
||||||
|
return self._fs2hookproxy[fspath]
|
||||||
|
except KeyError:
|
||||||
|
self._fs2hookproxy[fspath] = x = FSHookProxy(fspath, self.config)
|
||||||
|
return x
|
||||||
|
|
||||||
def perform_collect(self, args=None, genitems=True):
|
def perform_collect(self, args=None, genitems=True):
|
||||||
hook = self.config.hook
|
hook = self.config.hook
|
||||||
|
|
|
@ -353,12 +353,14 @@ class PyCollector(PyobjMixin, pytest.Collector):
|
||||||
fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
|
fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
|
||||||
metafunc = Metafunc(funcobj, fixtureinfo, self.config,
|
metafunc = Metafunc(funcobj, fixtureinfo, self.config,
|
||||||
cls=cls, module=module)
|
cls=cls, module=module)
|
||||||
gentesthook = self.config.hook.pytest_generate_tests
|
try:
|
||||||
extra = [module]
|
methods = [module.pytest_generate_tests]
|
||||||
if cls is not None:
|
except AttributeError:
|
||||||
extra.append(cls())
|
methods = []
|
||||||
plugins = self.getplugins() + extra
|
if hasattr(cls, "pytest_generate_tests"):
|
||||||
gentesthook.pcall(plugins, metafunc=metafunc)
|
methods.append(cls().pytest_generate_tests)
|
||||||
|
self.ihook.pytest_generate_tests.callextra(methods, metafunc=metafunc)
|
||||||
|
|
||||||
Function = self._getcustomclass("Function")
|
Function = self._getcustomclass("Function")
|
||||||
if not metafunc._calls:
|
if not metafunc._calls:
|
||||||
yield Function(name, parent=self)
|
yield Function(name, parent=self)
|
||||||
|
|
Loading…
Reference in New Issue