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:
holger krekel 2014-10-09 10:47:32 +02:00
parent d9858844c3
commit eda39f361d
2 changed files with 33 additions and 33 deletions

View File

@ -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)

View File

@ -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: