diff options
author | goodboy <291685+goodboy@users.noreply.github.com> | 2020-07-02 09:15:31 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-02 09:15:31 -0500 |
commit | 0a064fe275060dbdb1fe6e10c888e72bc400fb33 (patch) | |
tree | 278fcbcc1fee9f0eced27996fb61587ffe7853dd | |
parent | 656bc3d04d3b452d501cfd955c53c5e724a61bc5 (diff) | |
parent | 0aa2462ebd8639fe888a7a632b2f16727c2702b3 (diff) | |
download | pluggy-0a064fe275060dbdb1fe6e10c888e72bc400fb33.tar.gz |
Merge pull request #280 from bluetech/optimize-call2
Optimize hook calling a bit
-rw-r--r-- | src/pluggy/callers.py | 2 | ||||
-rw-r--r-- | src/pluggy/hooks.py | 34 | ||||
-rw-r--r-- | src/pluggy/manager.py | 20 | ||||
-rw-r--r-- | testing/benchmark.py | 25 | ||||
-rw-r--r-- | testing/test_multicall.py | 2 |
5 files changed, 44 insertions, 39 deletions
diff --git a/src/pluggy/callers.py b/src/pluggy/callers.py index f66cc94..f4719fa 100644 --- a/src/pluggy/callers.py +++ b/src/pluggy/callers.py @@ -6,7 +6,7 @@ import sys from ._result import HookCallError, _Result, _raise_wrapfail -def _multicall(hook_impls, caller_kwargs, firstresult=False): +def _multicall(hook_name, hook_impls, caller_kwargs, firstresult): """Execute a call into multiple python functions/methods and return the result(s). diff --git a/src/pluggy/hooks.py b/src/pluggy/hooks.py index 572c02e..cbd1022 100644 --- a/src/pluggy/hooks.py +++ b/src/pluggy/hooks.py @@ -247,15 +247,23 @@ class _HookCaller: raise TypeError("hook calling supports only keyword arguments") assert not self.is_historic() - if self.spec and self.spec.argnames: - notincall = set(self.spec.argnames) - set(kwargs.keys()) - if notincall: - warnings.warn( - "Argument(s) {} which are declared in the hookspec " - "can not be found in this hook call".format(tuple(notincall)), - stacklevel=2, - ) - return self._hookexec(self, self.get_hookimpls(), kwargs) + # This is written to avoid expensive operations when not needed. + if self.spec: + for argname in self.spec.argnames: + if argname not in kwargs: + notincall = tuple(set(self.spec.argnames) - kwargs.keys()) + warnings.warn( + "Argument(s) {} which are declared in the hookspec " + "can not be found in this hook call".format(notincall), + stacklevel=2, + ) + break + + firstresult = self.spec.opts.get("firstresult") + else: + firstresult = False + + return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult) def call_historic(self, result_callback=None, kwargs=None): """Call the hook with given ``kwargs`` for all registered plugins and @@ -265,11 +273,11 @@ class _HookCaller: non-``None`` result obtained from a hook implementation. """ self._call_history.append((kwargs or {}, result_callback)) - # historizing hooks don't return results - res = self._hookexec(self, self.get_hookimpls(), kwargs) + # Historizing hooks don't return results. + # Remember firstresult isn't compatible with historic. + res = self._hookexec(self.name, self.get_hookimpls(), kwargs, False) if result_callback is None: return - # XXX: remember firstresult isn't compat with historic for x in res or []: result_callback(x) @@ -291,7 +299,7 @@ class _HookCaller: """ if self.is_historic(): for kwargs, result_callback in self._call_history: - res = self._hookexec(self, [method], kwargs) + res = self._hookexec(self.name, [method], kwargs, False) if res and result_callback is not None: result_callback(res[0]) diff --git a/src/pluggy/manager.py b/src/pluggy/manager.py index 7f37822..bba2f95 100644 --- a/src/pluggy/manager.py +++ b/src/pluggy/manager.py @@ -71,16 +71,12 @@ class PluginManager: self._plugin_distinfo = [] self.trace = _tracing.TagTracer().get("pluginmanage") self.hook = _HookRelay() - self._inner_hookexec = lambda hook, methods, kwargs: _multicall( - methods, - kwargs, - firstresult=hook.spec.opts.get("firstresult") if hook.spec else False, - ) + self._inner_hookexec = _multicall - def _hookexec(self, hook, methods, kwargs): + def _hookexec(self, hook_name, methods, kwargs, firstresult): # called from all hookcaller instances. # enable_tracing will set its own wrapping function at self._inner_hookexec - return self._inner_hookexec(hook, methods, kwargs) + return self._inner_hookexec(hook_name, methods, kwargs, firstresult) def register(self, plugin, name=None): """ Register a plugin and return its canonical name or ``None`` if the name @@ -311,10 +307,12 @@ class PluginManager: """ oldcall = self._inner_hookexec - def traced_hookexec(hook, hook_impls, kwargs): - before(hook.name, hook_impls, kwargs) - outcome = _Result.from_call(lambda: oldcall(hook, hook_impls, kwargs)) - after(outcome, hook.name, hook_impls, kwargs) + def traced_hookexec(hook_name, hook_impls, kwargs, firstresult): + before(hook_name, hook_impls, kwargs) + outcome = _Result.from_call( + lambda: oldcall(hook_name, hook_impls, kwargs, firstresult) + ) + after(outcome, hook_name, hook_impls, kwargs) return outcome.get_result() self._inner_hookexec = traced_hookexec diff --git a/testing/benchmark.py b/testing/benchmark.py index cca4a75..7eec411 100644 --- a/testing/benchmark.py +++ b/testing/benchmark.py @@ -6,18 +6,11 @@ from pluggy import HookspecMarker, HookimplMarker from pluggy.hooks import HookImpl from pluggy.callers import _multicall + hookspec = HookspecMarker("example") hookimpl = HookimplMarker("example") -def MC(methods, kwargs, firstresult=False): - hookfuncs = [] - for method in methods: - f = HookImpl(None, "<temp>", method, method.example_impl) - hookfuncs.append(f) - return _multicall(hookfuncs, kwargs, firstresult=firstresult) - - @hookimpl def hook(arg1, arg2, arg3): return arg1, arg2, arg3 @@ -38,9 +31,15 @@ def wrappers(request): return [wrapper for i in range(request.param)] -def inner_exec(methods): - return MC(methods, {"arg1": 1, "arg2": 2, "arg3": 3}) - - def test_hook_and_wrappers_speed(benchmark, hooks, wrappers): - benchmark(inner_exec, hooks + wrappers) + def setup(): + hook_name = "foo" + hook_impls = [] + for method in hooks + wrappers: + f = HookImpl(None, "<temp>", method, method.example_impl) + hook_impls.append(f) + caller_kwargs = {"arg1": 1, "arg2": 2, "arg3": 3} + firstresult = False + return (hook_name, hook_impls, caller_kwargs, firstresult), {} + + benchmark.pedantic(_multicall, setup=setup) diff --git a/testing/test_multicall.py b/testing/test_multicall.py index 79f179f..554ed19 100644 --- a/testing/test_multicall.py +++ b/testing/test_multicall.py @@ -14,7 +14,7 @@ def MC(methods, kwargs, firstresult=False): for method in methods: f = HookImpl(None, "<temp>", method, method.example_impl) hookfuncs.append(f) - return caller(hookfuncs, kwargs, firstresult=firstresult) + return caller("foo", hookfuncs, kwargs, firstresult) def test_keyword_args(): |