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