9
9
from collections .abc import Sequence
10
10
from typing import cast
11
11
from typing import NoReturn
12
- from typing import Union
13
12
import warnings
14
13
15
14
from ._hooks import HookImpl
20
19
21
20
# Need to distinguish between old- and new-style hook wrappers.
22
21
# Wrapping with a tuple is the fastest type-safe way I found to do it.
23
- Teardown = Union [
24
- tuple [Generator [None , Result [object ], None ], HookImpl ],
25
- Generator [None , object , object ],
26
- ]
22
+ Teardown = Generator [None , object , object ]
23
+
24
+
25
+ def run_old_style_hookwrapper (
26
+ hook_impl : HookImpl , hook_name : str , args : Sequence [object ]
27
+ ) -> Teardown :
28
+ """
29
+ backward compatibility wrapper to run a old style hookwrapper as a wrapper
30
+ """
31
+
32
+ teardown : Teardown = cast (Teardown , hook_impl .function (* args ))
33
+ try :
34
+ next (teardown )
35
+ except StopIteration :
36
+ _raise_wrapfail (teardown , "did not yield" )
37
+ try :
38
+ res = yield
39
+ result = Result (res , None )
40
+ except BaseException as exc :
41
+ result = Result (None , exc )
42
+ try :
43
+ teardown .send (result )
44
+ except StopIteration :
45
+ pass
46
+ except BaseException as e :
47
+ _warn_teardown_exception (hook_name , hook_impl , e )
48
+ raise
49
+ else :
50
+ _raise_wrapfail (teardown , "has second yield" )
51
+ finally :
52
+ teardown .close ()
53
+ return result .get_result ()
27
54
28
55
29
56
def _raise_wrapfail (
30
- wrap_controller : (
31
- Generator [None , Result [object ], None ] | Generator [None , object , object ]
32
- ),
57
+ wrap_controller : Generator [None , object , object ],
33
58
msg : str ,
34
59
) -> NoReturn :
35
- co = wrap_controller .gi_code # type: ignore[union- attr]
60
+ co = wrap_controller .gi_code # type: ignore[attr-defined ]
36
61
raise RuntimeError (
37
62
f"wrap_controller at { co .co_name !r} { co .co_filename } :{ co .co_firstlineno } { msg } "
38
63
)
@@ -45,7 +70,7 @@ def _warn_teardown_exception(
45
70
msg += f"Plugin: { hook_impl .plugin_name } , Hook: { hook_name } \n "
46
71
msg += f"{ type (e ).__name__ } : { e } \n "
47
72
msg += "For more information see https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluggyTeardownRaisedWarning" # noqa: E501
48
- warnings .warn (PluggyTeardownRaisedWarning (msg ), stacklevel = 5 )
73
+ warnings .warn (PluggyTeardownRaisedWarning (msg ), stacklevel = 6 )
49
74
50
75
51
76
def _multicall (
@@ -62,31 +87,26 @@ def _multicall(
62
87
__tracebackhide__ = True
63
88
results : list [object ] = []
64
89
exception = None
65
- only_new_style_wrappers = True
66
90
try : # run impl and wrapper setup functions in a loop
67
91
teardowns : list [Teardown ] = []
68
92
try :
69
93
for hook_impl in reversed (hook_impls ):
70
94
try :
71
95
args = [caller_kwargs [argname ] for argname in hook_impl .argnames ]
72
- except KeyError :
73
- for argname in hook_impl .argnames :
96
+ except KeyError as e :
97
+ # coverage bug - this is tested
98
+ for argname in hook_impl .argnames : # pragma: no cover
74
99
if argname not in caller_kwargs :
75
100
raise HookCallError (
76
101
f"hook call must provide argument { argname !r} "
77
- )
102
+ ) from e
78
103
79
104
if hook_impl .hookwrapper :
80
- only_new_style_wrappers = False
81
- try :
82
- # If this cast is not valid, a type error is raised below,
83
- # which is the desired response.
84
- res = hook_impl .function (* args )
85
- wrapper_gen = cast (Generator [None , Result [object ], None ], res )
86
- next (wrapper_gen ) # first yield
87
- teardowns .append ((wrapper_gen , hook_impl ))
88
- except StopIteration :
89
- _raise_wrapfail (wrapper_gen , "did not yield" )
105
+ function_gen = run_old_style_hookwrapper (hook_impl , hook_name , args )
106
+
107
+ next (function_gen ) # first yield
108
+ teardowns .append (function_gen )
109
+
90
110
elif hook_impl .wrapper :
91
111
try :
92
112
# If this cast is not valid, a type error is raised below,
@@ -106,99 +126,44 @@ def _multicall(
106
126
except BaseException as exc :
107
127
exception = exc
108
128
finally :
109
- # Fast path - only new-style wrappers, no Result.
110
- if only_new_style_wrappers :
111
- if firstresult : # first result hooks return a single value
112
- result = results [0 ] if results else None
113
- else :
114
- result = results
115
-
116
- # run all wrapper post-yield blocks
117
- for teardown in reversed (teardowns ):
118
- try :
119
- if exception is not None :
120
- try :
121
- teardown .throw (exception ) # type: ignore[union-attr]
122
- except RuntimeError as re :
123
- # StopIteration from generator causes RuntimeError
124
- # even for coroutine usage - see #544
125
- if (
126
- isinstance (exception , StopIteration )
127
- and re .__cause__ is exception
128
- ):
129
- teardown .close () # type: ignore[union-attr]
130
- continue
131
- else :
132
- raise
133
- else :
134
- teardown .send (result ) # type: ignore[union-attr]
135
- # Following is unreachable for a well behaved hook wrapper.
136
- # Try to force finalizers otherwise postponed till GC action.
137
- # Note: close() may raise if generator handles GeneratorExit.
138
- teardown .close () # type: ignore[union-attr]
139
- except StopIteration as si :
140
- result = si .value
141
- exception = None
142
- continue
143
- except BaseException as e :
144
- exception = e
145
- continue
146
- _raise_wrapfail (teardown , "has second yield" ) # type: ignore[arg-type]
147
-
148
- if exception is not None :
149
- raise exception
150
- else :
151
- return result
152
-
153
- # Slow path - need to support old-style wrappers.
129
+ if firstresult : # first result hooks return a single value
130
+ result = results [0 ] if results else None
154
131
else :
155
- if firstresult : # first result hooks return a single value
156
- outcome : Result [object | list [object ]] = Result (
157
- results [0 ] if results else None , exception
158
- )
159
- else :
160
- outcome = Result (results , exception )
161
-
162
- # run all wrapper post-yield blocks
163
- for teardown in reversed (teardowns ):
164
- if isinstance (teardown , tuple ):
165
- try :
166
- teardown [0 ].send (outcome )
167
- except StopIteration :
168
- pass
169
- except BaseException as e :
170
- _warn_teardown_exception (hook_name , teardown [1 ], e )
171
- raise
172
- else :
173
- _raise_wrapfail (teardown [0 ], "has second yield" )
174
- else :
132
+ result = results
133
+
134
+ # run all wrapper post-yield blocks
135
+ for teardown in reversed (teardowns ):
136
+ try :
137
+ if exception is not None :
175
138
try :
176
- if outcome ._exception is not None :
177
- try :
178
- teardown .throw (outcome ._exception )
179
- except RuntimeError as re :
180
- # StopIteration from generator causes RuntimeError
181
- # even for coroutine usage - see #544
182
- if (
183
- isinstance (outcome ._exception , StopIteration )
184
- and re .__cause__ is outcome ._exception
185
- ):
186
- teardown .close ()
187
- continue
188
- else :
189
- raise
139
+ teardown .throw (exception )
140
+ except RuntimeError as re :
141
+ # StopIteration from generator causes RuntimeError
142
+ # even for coroutine usage - see #544
143
+ if (
144
+ isinstance (exception , StopIteration )
145
+ and re .__cause__ is exception
146
+ ):
147
+ teardown .close ()
148
+ continue
190
149
else :
191
- teardown .send (outcome ._result )
192
- # Following is unreachable for a well behaved hook wrapper.
193
- # Try to force finalizers otherwise postponed till GC action.
194
- # Note: close() may raise if generator handles GeneratorExit.
195
- teardown .close ()
196
- except StopIteration as si :
197
- outcome .force_result (si .value )
198
- continue
199
- except BaseException as e :
200
- outcome .force_exception (e )
201
- continue
202
- _raise_wrapfail (teardown , "has second yield" )
203
-
204
- return outcome .get_result ()
150
+ raise
151
+ else :
152
+ teardown .send (result )
153
+ # Following is unreachable for a well behaved hook wrapper.
154
+ # Try to force finalizers otherwise postponed till GC action.
155
+ # Note: close() may raise if generator handles GeneratorExit.
156
+ teardown .close ()
157
+ except StopIteration as si :
158
+ result = si .value
159
+ exception = None
160
+ continue
161
+ except BaseException as e :
162
+ exception = e
163
+ continue
164
+ _raise_wrapfail (teardown , "has second yield" )
165
+
166
+ if exception is not None :
167
+ raise exception
168
+ else :
169
+ return result
0 commit comments