re-scan methods during plugin register and unregister and not
during hook calling anymore. Simplify register/getplugin api of PluginManager
This commit is contained in:
parent
d9858844c3
commit
eda39f361d
|
@ -139,6 +139,7 @@ class PluginManager(object):
|
||||||
self._name2plugin = {}
|
self._name2plugin = {}
|
||||||
self._plugins = []
|
self._plugins = []
|
||||||
self._conftestplugins = []
|
self._conftestplugins = []
|
||||||
|
self._plugin2hookcallers = {}
|
||||||
self._warnings = []
|
self._warnings = []
|
||||||
self.trace = TagTracer().get("pluginmanage")
|
self.trace = TagTracer().get("pluginmanage")
|
||||||
self._plugin_distinfo = []
|
self._plugin_distinfo = []
|
||||||
|
@ -182,7 +183,8 @@ 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) # may call addhooks
|
reg(plugin, name) # may call addhooks
|
||||||
self.hook._scan_plugin(plugin)
|
hookcallers = list(self.hook._scan_plugin(plugin))
|
||||||
|
self._plugin2hookcallers[plugin] = hookcallers
|
||||||
self._name2plugin[name] = plugin
|
self._name2plugin[name] = plugin
|
||||||
if conftest:
|
if conftest:
|
||||||
self._conftestplugins.append(plugin)
|
self._conftestplugins.append(plugin)
|
||||||
|
@ -191,11 +193,12 @@ class PluginManager(object):
|
||||||
self._plugins.append(plugin)
|
self._plugins.append(plugin)
|
||||||
else:
|
else:
|
||||||
self._plugins.insert(0, plugin)
|
self._plugins.insert(0, plugin)
|
||||||
|
# finally make sure that the methods of the new plugin take part
|
||||||
|
for hookcaller in hookcallers:
|
||||||
|
hookcaller.scan_methods()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def unregister(self, plugin=None, name=None):
|
def unregister(self, plugin):
|
||||||
if plugin is None:
|
|
||||||
plugin = self.getplugin(name=name)
|
|
||||||
try:
|
try:
|
||||||
self._plugins.remove(plugin)
|
self._plugins.remove(plugin)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -203,6 +206,9 @@ class PluginManager(object):
|
||||||
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]
|
||||||
|
hookcallers = self._plugin2hookcallers.pop(plugin)
|
||||||
|
for hookcaller in hookcallers:
|
||||||
|
hookcaller.scan_methods()
|
||||||
|
|
||||||
def add_shutdown(self, func):
|
def add_shutdown(self, func):
|
||||||
self._shutdown.append(func)
|
self._shutdown.append(func)
|
||||||
|
@ -217,9 +223,7 @@ class PluginManager(object):
|
||||||
def isregistered(self, plugin, name=None):
|
def isregistered(self, plugin, name=None):
|
||||||
if self.getplugin(name) is not None:
|
if self.getplugin(name) is not None:
|
||||||
return True
|
return True
|
||||||
for val in self._name2plugin.values():
|
return plugin in self._plugins or plugin in self._conftestplugins
|
||||||
if plugin == val:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def addhooks(self, spec, prefix="pytest_"):
|
def addhooks(self, spec, prefix="pytest_"):
|
||||||
self.hook._addhooks(spec, prefix=prefix)
|
self.hook._addhooks(spec, prefix=prefix)
|
||||||
|
@ -281,8 +285,9 @@ class PluginManager(object):
|
||||||
def consider_pluginarg(self, arg):
|
def consider_pluginarg(self, arg):
|
||||||
if arg.startswith("no:"):
|
if arg.startswith("no:"):
|
||||||
name = arg[3:]
|
name = arg[3:]
|
||||||
if self.getplugin(name) is not None:
|
plugin = self.getplugin(name)
|
||||||
self.unregister(None, name=name)
|
if plugin is not None:
|
||||||
|
self.unregister(plugin)
|
||||||
self._name2plugin[name] = -1
|
self._name2plugin[name] = -1
|
||||||
else:
|
else:
|
||||||
if self.getplugin(arg) is None:
|
if self.getplugin(arg) is None:
|
||||||
|
@ -485,18 +490,18 @@ class HookRelay:
|
||||||
"available hookargs: %s",
|
"available hookargs: %s",
|
||||||
arg, formatdef(method),
|
arg, formatdef(method),
|
||||||
", ".join(hook.argnames))
|
", ".join(hook.argnames))
|
||||||
getattr(self, name).clear_method_cache()
|
yield hook
|
||||||
|
|
||||||
|
|
||||||
class HookCaller:
|
class HookCaller:
|
||||||
def __init__(self, hookrelay, name, firstresult, argnames, methods=None):
|
def __init__(self, hookrelay, name, firstresult, argnames, methods=()):
|
||||||
self.hookrelay = hookrelay
|
self.hookrelay = hookrelay
|
||||||
self.name = name
|
self.name = name
|
||||||
self.firstresult = firstresult
|
self.firstresult = firstresult
|
||||||
self.methods = methods
|
|
||||||
self.argnames = ["__multicall__"]
|
self.argnames = ["__multicall__"]
|
||||||
self.argnames.extend(argnames)
|
self.argnames.extend(argnames)
|
||||||
assert "self" not in argnames # prevent oversights
|
assert "self" not in argnames # sanity check
|
||||||
|
self.methods = methods
|
||||||
|
|
||||||
def new_cached_caller(self, methods):
|
def new_cached_caller(self, methods):
|
||||||
return HookCaller(self.hookrelay, self.name, self.firstresult,
|
return HookCaller(self.hookrelay, self.name, self.firstresult,
|
||||||
|
@ -505,14 +510,11 @@ class HookCaller:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<HookCaller %r>" %(self.name,)
|
return "<HookCaller %r>" %(self.name,)
|
||||||
|
|
||||||
def clear_method_cache(self):
|
def scan_methods(self):
|
||||||
self.methods = None
|
self.methods = self.hookrelay._pm.listattr(self.name)
|
||||||
|
|
||||||
def __call__(self, **kwargs):
|
def __call__(self, **kwargs):
|
||||||
methods = self.methods
|
return self._docall(self.methods, kwargs)
|
||||||
if methods is None:
|
|
||||||
self.methods = methods = self.hookrelay._pm.listattr(self.name)
|
|
||||||
return self._docall(methods, kwargs)
|
|
||||||
|
|
||||||
def callextra(self, methods, **kwargs):
|
def callextra(self, methods, **kwargs):
|
||||||
return self._docall(self.methods + methods, kwargs)
|
return self._docall(self.methods + methods, kwargs)
|
||||||
|
|
|
@ -184,8 +184,6 @@ class TestBootstrapping:
|
||||||
assert pp.getplugin('hello') == a2
|
assert pp.getplugin('hello') == a2
|
||||||
pp.unregister(a1)
|
pp.unregister(a1)
|
||||||
assert not pp.isregistered(a1)
|
assert not pp.isregistered(a1)
|
||||||
pp.unregister(name="hello")
|
|
||||||
assert not pp.isregistered(a2)
|
|
||||||
|
|
||||||
def test_pm_ordering(self):
|
def test_pm_ordering(self):
|
||||||
pp = PluginManager()
|
pp = PluginManager()
|
||||||
|
@ -612,21 +610,23 @@ class TestMultiCall:
|
||||||
|
|
||||||
class TestHookRelay:
|
class TestHookRelay:
|
||||||
def test_happypath(self):
|
def test_happypath(self):
|
||||||
pm = PluginManager()
|
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
"api hook 1"
|
"api hook 1"
|
||||||
|
pm = PluginManager([Api], prefix="he")
|
||||||
mcm = HookRelay(hookspecs=Api, pm=pm, prefix="he")
|
hook = pm.hook
|
||||||
assert hasattr(mcm, 'hello')
|
assert hasattr(hook, 'hello')
|
||||||
assert repr(mcm.hello).find("hello") != -1
|
assert repr(hook.hello).find("hello") != -1
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
return arg + 1
|
return arg + 1
|
||||||
pm.register(Plugin())
|
plugin = Plugin()
|
||||||
l = mcm.hello(arg=3)
|
pm.register(plugin)
|
||||||
|
l = hook.hello(arg=3)
|
||||||
assert l == [4]
|
assert l == [4]
|
||||||
assert not hasattr(mcm, 'world')
|
assert not hasattr(hook, 'world')
|
||||||
|
pm.unregister(plugin)
|
||||||
|
assert hook.hello(arg=3) == []
|
||||||
|
|
||||||
def test_argmismatch(self):
|
def test_argmismatch(self):
|
||||||
class Api:
|
class Api:
|
||||||
|
@ -649,18 +649,16 @@ class TestHookRelay:
|
||||||
pytest.raises(TypeError, lambda: mcm.hello(3))
|
pytest.raises(TypeError, lambda: mcm.hello(3))
|
||||||
|
|
||||||
def test_firstresult_definition(self):
|
def test_firstresult_definition(self):
|
||||||
pm = PluginManager()
|
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
"api hook 1"
|
"api hook 1"
|
||||||
hello.firstresult = True
|
hello.firstresult = True
|
||||||
|
pm = PluginManager([Api], "he")
|
||||||
mcm = HookRelay(hookspecs=Api, pm=pm, prefix="he")
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
return arg + 1
|
return arg + 1
|
||||||
pm.register(Plugin())
|
pm.register(Plugin())
|
||||||
res = mcm.hello(arg=3)
|
res = pm.hook.hello(arg=3)
|
||||||
assert res == 4
|
assert res == 4
|
||||||
|
|
||||||
class TestTracer:
|
class TestTracer:
|
||||||
|
|
Loading…
Reference in New Issue