diff --git a/src/pluggy/_callers.py b/src/pluggy/_callers.py index 7a16f3bd..d2189add 100644 --- a/src/pluggy/_callers.py +++ b/src/pluggy/_callers.py @@ -15,46 +15,47 @@ def _multicall(hook_name, hook_impls, caller_kwargs, firstresult): __tracebackhide__ = True results = [] excinfo = None - try: # run impl and wrapper setup functions in a loop - teardowns = [] - try: - for hook_impl in reversed(hook_impls): - try: - args = [caller_kwargs[argname] for argname in hook_impl.argnames] - except KeyError: - for argname in hook_impl.argnames: - if argname not in caller_kwargs: - raise HookCallError( - f"hook call must provide argument {argname!r}" - ) - - 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 is not None: - results.append(res) - if firstresult: # halt further impl calls - break - except BaseException: - excinfo = sys.exc_info() - finally: - if firstresult: # first result hooks return a single value - outcome = _Result(results[0] if results else None, excinfo) - else: - outcome = _Result(results, excinfo) - - # run all wrapper post-yield blocks - for gen in reversed(teardowns): + + # run impl and wrapper setup functions in a loop + teardowns = [] + try: + for hook_impl in reversed(hook_impls): try: - gen.send(outcome) - _raise_wrapfail(gen, "has second yield") - except StopIteration: - pass + args = [caller_kwargs[argname] for argname in hook_impl.argnames] + except KeyError: + for argname in hook_impl.argnames: + if argname not in caller_kwargs: + raise HookCallError( + "hook call must provide argument %r" % (argname,) + ) + + 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 is not None: + results.append(res) + if firstresult: # halt further impl calls + break + except BaseException: + excinfo = sys.exc_info() + + if firstresult: # first result hooks return a single value + outcome = _Result(results[0] if results else None, excinfo) + else: + outcome = _Result(results, excinfo) + + # run all wrapper post-yield blocks + for gen in reversed(teardowns): + try: + gen.send(outcome) + _raise_wrapfail(gen, "has second yield") + except StopIteration: + pass - return outcome.get_result() + return outcome.get_result()