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