@@ -77,67 +77,128 @@ def get_result(self):
77
77
_reraise (* ex ) # noqa
78
78
79
79
80
- class _MultiCall (object ):
81
- """Execute a call into multiple python functions/methods.
80
+ def _wrapped_call (wrap_controller , func ):
81
+ """ Wrap calling to a function with a generator which needs to yield
82
+ exactly once. The yield point will trigger calling the wrapped function
83
+ and return its ``_Result`` to the yield point. The generator then needs
84
+ to finish (raise StopIteration) in order for the wrapped call to complete.
82
85
"""
86
+ try :
87
+ next (wrap_controller ) # first yield
88
+ except StopIteration :
89
+ _raise_wrapfail (wrap_controller , "did not yield" )
90
+ call_outcome = _Result .from_call (func )
91
+ try :
92
+ wrap_controller .send (call_outcome )
93
+ _raise_wrapfail (wrap_controller , "has second yield" )
94
+ except StopIteration :
95
+ pass
96
+ return call_outcome .get_result ()
97
+
98
+
99
+ class _LegacyMultiCall (object ):
100
+ """ execute a call into multiple python functions/methods. """
101
+
102
+ # XXX note that the __multicall__ argument is supported only
103
+ # for pytest compatibility reasons. It was never officially
104
+ # supported there and is explicitely deprecated since 2.8
105
+ # so we can remove it soon, allowing to avoid the below recursion
106
+ # in execute() and simplify/speed up the execute loop.
107
+
83
108
def __init__ (self , hook_impls , kwargs , specopts = {}, hook = None ):
84
109
self .hook = hook
85
110
self .hook_impls = hook_impls
86
111
self .caller_kwargs = kwargs # come from _HookCaller.__call__()
112
+ self .caller_kwargs ["__multicall__" ] = self
87
113
self .specopts = hook .spec_opts if hook else specopts
88
114
89
115
def execute (self ):
90
- __tracebackhide__ = True
91
116
caller_kwargs = self .caller_kwargs
92
117
self .results = results = []
93
118
firstresult = self .specopts .get ("firstresult" )
94
- excinfo = None
95
- try : # run impl and wrapper setup functions in a loop
96
- teardowns = []
97
- try :
98
- for hook_impl in reversed (self .hook_impls ):
99
- try :
100
- args = [caller_kwargs [argname ] for argname in hook_impl .argnames ]
101
- # args = operator.itemgetter(hookimpl.argnames)(caller_kwargs)
102
- except KeyError :
103
- for argname in hook_impl .argnames :
104
- if argname not in caller_kwargs :
105
- raise HookCallError (
106
- "hook call must provide argument %r" % (argname ,))
107
-
108
- if hook_impl .hookwrapper :
109
- try :
110
- gen = hook_impl .function (* args )
111
- next (gen ) # first yield
112
- teardowns .append (gen )
113
- except StopIteration :
114
- _raise_wrapfail (gen , "did not yield" )
115
- else :
116
- res = hook_impl .function (* args )
117
- if res is not None :
118
- results .append (res )
119
- if firstresult : # halt further impl calls
120
- break
121
- except BaseException :
122
- excinfo = sys .exc_info ()
123
- finally :
124
- if firstresult : # first result hooks return a single value
125
- outcome = _Result (results [0 ] if results else None , excinfo )
126
- else :
127
- outcome = _Result (results , excinfo )
128
-
129
- # run all wrapper post-yield blocks
130
- for gen in reversed (teardowns ):
131
- try :
132
- gen .send (outcome )
133
- _raise_wrapfail (gen , "has second yield" )
134
- except StopIteration :
135
- pass
136
119
137
- return outcome .get_result ()
120
+ while self .hook_impls :
121
+ hook_impl = self .hook_impls .pop ()
122
+ try :
123
+ args = [caller_kwargs [argname ] for argname in hook_impl .argnames ]
124
+ except KeyError :
125
+ for argname in hook_impl .argnames :
126
+ if argname not in caller_kwargs :
127
+ raise HookCallError (
128
+ "hook call must provide argument %r" % (argname ,))
129
+ if hook_impl .hookwrapper :
130
+ return _wrapped_call (hook_impl .function (* args ), self .execute )
131
+ res = hook_impl .function (* args )
132
+ if res is not None :
133
+ if firstresult :
134
+ return res
135
+ results .append (res )
136
+
137
+ if not firstresult :
138
+ return results
138
139
139
140
def __repr__ (self ):
140
141
status = "%d meths" % (len (self .hook_impls ),)
141
142
if hasattr (self , "results" ):
142
143
status = ("%d results, " % len (self .results )) + status
143
144
return "<_MultiCall %s, kwargs=%r>" % (status , self .caller_kwargs )
145
+
146
+
147
+ def _legacymulticall (hook_impls , caller_kwargs , specopts = {}, hook = None ):
148
+ return _LegacyMultiCall (
149
+ hook_impls , caller_kwargs , specopts = specopts , hook = hook ).execute ()
150
+
151
+
152
+ def _multicall (hook_impls , caller_kwargs , specopts = {}, hook = None ):
153
+ """Execute a call into multiple python functions/methods and return the
154
+ result(s).
155
+
156
+ ``caller_kwargs`` comes from _HookCaller.__call__().
157
+ """
158
+ __tracebackhide__ = True
159
+ specopts = hook .spec_opts if hook else specopts
160
+ results = []
161
+ firstresult = specopts .get ("firstresult" )
162
+ excinfo = None
163
+ try : # run impl and wrapper setup functions in a loop
164
+ teardowns = []
165
+ try :
166
+ for hook_impl in reversed (hook_impls ):
167
+ try :
168
+ args = [caller_kwargs [argname ] for argname in hook_impl .argnames ]
169
+ except KeyError :
170
+ for argname in hook_impl .argnames :
171
+ if argname not in caller_kwargs :
172
+ raise HookCallError (
173
+ "hook call must provide argument %r" % (argname ,))
174
+
175
+ if hook_impl .hookwrapper :
176
+ try :
177
+ gen = hook_impl .function (* args )
178
+ next (gen ) # first yield
179
+ teardowns .append (gen )
180
+ except StopIteration :
181
+ _raise_wrapfail (gen , "did not yield" )
182
+ else :
183
+ res = hook_impl .function (* args )
184
+ if res is not None :
185
+ results .append (res )
186
+ if firstresult : # halt further impl calls
187
+ break
188
+ except BaseException :
189
+ excinfo = sys .exc_info ()
190
+ finally :
191
+ if firstresult : # first result hooks return a single value
192
+ outcome = _Result (results [0 ] if results else None , excinfo )
193
+ else :
194
+ outcome = _Result (results , excinfo )
195
+
196
+ # run all wrapper post-yield blocks
197
+ for gen in reversed (teardowns ):
198
+ try :
199
+ gen .send (outcome )
200
+ _raise_wrapfail (gen , "has second yield" )
201
+ except StopIteration :
202
+ pass
203
+
204
+ return outcome .get_result ()
0 commit comments