diff --git a/pluggy/callers.py b/pluggy/callers.py index c5934b96..642bbcc2 100644 --- a/pluggy/callers.py +++ b/pluggy/callers.py @@ -96,7 +96,10 @@ def execute(self): except BaseException: excinfo = sys.exc_info() finally: - outcome = _Result(results, excinfo) + 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): @@ -106,10 +109,6 @@ def execute(self): except StopIteration: pass - if firstresult: - result = outcome.get_result() - return result[0] if result else None - return outcome.get_result() def __repr__(self): diff --git a/testing/test_hookrelay.py b/testing/test_hookrelay.py index 3653151b..7d9b6f52 100644 --- a/testing/test_hookrelay.py +++ b/testing/test_hookrelay.py @@ -87,13 +87,56 @@ class Plugin3(object): def hello(self, arg): return None + class Plugin4(object): + @hookimpl(hookwrapper=True) + def hello(self, arg): + assert arg == 3 + outcome = yield + assert outcome.get_result() == 2 + pm.register(Plugin1()) # discarded - not the last registered plugin pm.register(Plugin2()) # used as result pm.register(Plugin3()) # None result is ignored + pm.register(Plugin4()) # hookwrapper should get same non-list result res = pm.hook.hello(arg=3) assert res == 2 +def test_firstresult_force_result(pm): + """Verify forcing a result in a wrapper. + """ + class Api(object): + @hookspec(firstresult=True) + def hello(self, arg): + "api hook 1" + + pm.add_hookspecs(Api) + + class Plugin1(object): + @hookimpl + def hello(self, arg): + return arg + 1 + + class Plugin2(object): + @hookimpl(hookwrapper=True) + def hello(self, arg): + assert arg == 3 + outcome = yield + assert outcome.get_result() == 4 + outcome.force_result(0) + + class Plugin3(object): + @hookimpl + def hello(self, arg): + return None + + pm.register(Plugin1()) + pm.register(Plugin2()) # wrapper + pm.register(Plugin3()) # ignored since returns None + res = pm.hook.hello(arg=3) + assert res == 0 # this result is forced and not a list + + def test_firstresult_returns_none(pm): """If None results are returned by underlying implementations ensure the multi-call loop returns a None value.