Skip to content

Commit 14e84a5

Browse files
author
Tyler Goodlet
committed
WIP: Draft iterable hooks implementation
1 parent ab41599 commit 14e84a5

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

pluggy/callers.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,60 @@ def _multicall(hook_impls, caller_kwargs, firstresult=False):
199199
pass
200200

201201
return outcome.get_result()
202+
203+
204+
def _itercall(hook_impls, caller_kwargs, specopts={}, hook=None):
205+
"""Execute a calls into multiple python functions/methods and yield
206+
the result(s) lazily.
207+
208+
``caller_kwargs`` comes from _HookCaller.__call__().
209+
"""
210+
__tracebackhide__ = True
211+
specopts = hook.spec_opts if hook else specopts
212+
results = []
213+
firstresult = specopts.get("firstresult")
214+
excinfo = None
215+
try: # run impl and wrapper setup functions in a loop
216+
teardowns = []
217+
try:
218+
for hook_impl in reversed(hook_impls):
219+
try:
220+
args = [caller_kwargs[argname] for argname in hook_impl.argnames]
221+
except KeyError:
222+
for argname in hook_impl.argnames:
223+
if argname not in caller_kwargs:
224+
raise HookCallError(
225+
"hook call must provide argument %r" % (argname,))
226+
227+
if hook_impl.hookwrapper:
228+
try:
229+
gen = hook_impl.function(*args)
230+
next(gen) # first yield
231+
teardowns.append(gen)
232+
except StopIteration:
233+
_raise_wrapfail(gen, "did not yield")
234+
else:
235+
res = hook_impl.function(*args)
236+
if res is not None:
237+
results.append(res)
238+
yield res
239+
if firstresult: # halt further impl calls
240+
break
241+
except BaseException:
242+
excinfo = sys.exc_info()
243+
finally:
244+
if firstresult: # first result hooks return a single value
245+
outcome = _Result(results[0] if results else None, excinfo)
246+
else:
247+
outcome = _Result(results, excinfo)
248+
249+
# run all wrapper post-yield blocks
250+
for gen in reversed(teardowns):
251+
try:
252+
gen.send(outcome)
253+
_raise_wrapfail(gen, "has second yield")
254+
except StopIteration:
255+
pass
256+
257+
# raise any exceptions
258+
outcome.get_result()

pluggy/hooks.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,16 @@ def __init__(self, trace):
168168

169169

170170
class _HookCaller(object):
171-
def __init__(self, name, hook_execute, specmodule_or_class=None,
172-
spec_opts=None):
171+
def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None,
172+
iterate=False):
173173
self.name = name
174174
self._wrappers = []
175175
self._nonwrappers = []
176176
self._hookexec = hook_execute
177177
self._specmodule_or_class = None
178178
self.argnames = None
179179
self.kwargnames = None
180-
self.multicall = _multicall
180+
self.multicall = _multicall if not iterate else _itercall
181181
self.spec_opts = spec_opts or {}
182182
if specmodule_or_class is not None:
183183
self.set_specification(specmodule_or_class, spec_opts)

0 commit comments

Comments
 (0)