def_add_hookimpl(self, hookimpl): """Add an implementation to the callback chain. """ if hookimpl.hookwrapper: methods = self._wrappers else: methods = self._nonwrappers
if hookimpl.trylast: methods.insert(0, hookimpl) elif hookimpl.tryfirst: methods.append(hookimpl)
def__call__(self, *args, **kwargs): if args: raise TypeError("hook calling supports only keyword arguments") assertnotself.is_historic() ifself.spec andself.spec.argnames: notincall = ( set(self.spec.argnames) - set(["__multicall__"]) - 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, ) returnself._hookexec(self, self.get_hookimpls(), kwargs)
def_multicall(hook_impls, caller_kwargs, firstresult=False): """Execute a call into multiple python functions/methods and return the result(s). ``caller_kwargs`` comes from _HookCaller.__call__(). """ __tracebackhide__ = True results = [] excinfo = None try: # run impl and wrapper setup functions in a loop teardowns = [] try: for hook_impl inreversed(hook_impls): try: args = [caller_kwargs[argname] for argname in hook_impl.argnames] except KeyError: for argname in hook_impl.argnames: if argname notin caller_kwargs: raise HookCallError( "hook call must provide argument %r" % (argname,) )
原来这里还是会再检查调用的参数,如果spec定义的参数没有在kwargs中,这次是报错了
然后就是根据是否是hookwrapper来区分调用逻辑,我们先看下也应该正确的调用逻辑
hookwrapper
先调用 hookwrapper的plugin,先执行yield之前的代码
然后执行其他 plugin
最后把其他的plugin的执行结果作为参数传回到hookwrapper的plugin的 yield point ,继续执行
nonwrapper
直接调用 plugin ,并把结果返回到 result list中
如果有 firstresult,就直接返回
我们看看代码的实现
1 2 3 4 5 6 7 8 9 10 11 12 13
if hook_impl.hookwrapper: try: gen = hook_impl.function(*args) next(gen) # first yield teardowns.append(gen) except StopIteration: _raise_wrapfail(gen, "did not yield") else: res = hook_impl.function(*args) if res isnotNone: results.append(res) if firstresult: # halt further impl calls break
finally: if firstresult: # first result hooks return a single value outcome = _Result(results[0] if results elseNone, excinfo) else: outcome = _Result(results, excinfo)
# run all wrapper post-yield blocks for gen inreversed(teardowns): try: gen.send(outcome) _raise_wrapfail(gen, "has second yield") except StopIteration: pass
return outcome.get_result()
所有的结果都要用 _Result来封装返回
如果是 hookwrapper的,还要遍历之前存的generator, 把nonwrapper的结果回调回去 gen.send(output),为了避免实现错误,例如在plugin里写了两个yield, 直接在外部调用逻辑里抛出异常终止逻辑_raise_wrapfail(gen, "has second yield")