@@ -199,3 +199,60 @@ def _multicall(hook_impls, caller_kwargs, firstresult=False):
199
199
pass
200
200
201
201
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 ()
0 commit comments