6
6
7
7
from collections .abc import Generator
8
8
from collections .abc import Mapping
9
- from collections .abc import Sequence
10
9
from typing import cast
11
10
from typing import NoReturn
12
- from typing import Union
11
+ from typing import Sequence
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_legacy_hookwrapper (
26
+ hook_impl : HookImpl , hook_name : str , args : Sequence [object ]
27
+ ) -> Teardown :
28
+ teardown : Teardown = cast (Teardown , hook_impl .function (* args ))
29
+ try :
30
+ next (teardown )
31
+ except StopIteration :
32
+ _raise_wrapfail (teardown , "did not yield" )
33
+ try :
34
+ res = yield
35
+ result = Result (res , None )
36
+ except BaseException as exc :
37
+ result = Result (None , exc )
38
+ try :
39
+ teardown .send (result )
40
+ except StopIteration :
41
+ return result .get_result ()
42
+ except BaseException as e :
43
+ _warn_teardown_exception (hook_name , hook_impl , e )
44
+ raise
45
+ else :
46
+ _raise_wrapfail (teardown , "has second yield" )
47
+ finally :
48
+ teardown .close ()
27
49
28
50
29
51
def _raise_wrapfail (
@@ -45,7 +67,7 @@ def _warn_teardown_exception(
45
67
msg += f"Plugin: { hook_impl .plugin_name } , Hook: { hook_name } \n "
46
68
msg += f"{ type (e ).__name__ } : { e } \n "
47
69
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 )
70
+ warnings .warn (PluggyTeardownRaisedWarning (msg ), stacklevel = 6 )
49
71
50
72
51
73
def _multicall (
@@ -62,7 +84,6 @@ def _multicall(
62
84
__tracebackhide__ = True
63
85
results : list [object ] = []
64
86
exception = None
65
- only_new_style_wrappers = True
66
87
try : # run impl and wrapper setup functions in a loop
67
88
teardowns : list [Teardown ] = []
68
89
try :
@@ -77,16 +98,17 @@ def _multicall(
77
98
)
78
99
79
100
if hook_impl .hookwrapper :
80
- only_new_style_wrappers = False
81
101
try :
82
102
# If this cast is not valid, a type error is raised below,
83
103
# 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 ))
104
+ function_gen = run_legacy_hookwrapper (
105
+ hook_impl , hook_name , args
106
+ )
107
+
108
+ next (function_gen ) # first yield
109
+ teardowns .append (function_gen )
88
110
except StopIteration :
89
- _raise_wrapfail (wrapper_gen , "did not yield" )
111
+ _raise_wrapfail (function_gen , "did not yield" )
90
112
elif hook_impl .wrapper :
91
113
try :
92
114
# If this cast is not valid, a type error is raised below,
@@ -106,99 +128,44 @@ def _multicall(
106
128
except BaseException as exc :
107
129
exception = exc
108
130
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.
131
+ if firstresult : # first result hooks return a single value
132
+ result = results [0 ] if results else None
154
133
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 :
134
+ result = results
135
+
136
+ # run all wrapper post-yield blocks
137
+ for teardown in reversed (teardowns ):
138
+ try :
139
+ if exception is not None :
175
140
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
141
+ teardown .throw (exception )
142
+ except RuntimeError as re :
143
+ # StopIteration from generator causes RuntimeError
144
+ # even for coroutine usage - see #544
145
+ if (
146
+ isinstance (exception , StopIteration )
147
+ and re .__cause__ is exception
148
+ ):
149
+ teardown .close ()
150
+ continue
190
151
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 ()
152
+ raise
153
+ else :
154
+ teardown .send (result )
155
+ # Following is unreachable for a well behaved hook wrapper.
156
+ # Try to force finalizers otherwise postponed till GC action.
157
+ # Note: close() may raise if generator handles GeneratorExit.
158
+ teardown .close ()
159
+ except StopIteration as si :
160
+ result = si .value
161
+ exception = None
162
+ continue
163
+ except BaseException as e :
164
+ exception = e
165
+ continue
166
+ _raise_wrapfail (teardown , "has second yield" )
167
+
168
+ if exception is not None :
169
+ raise exception
170
+ else :
171
+ return result
0 commit comments