99from collections .abc import Sequence
1010from typing import cast
1111from typing import NoReturn
12- from typing import Union
1312import warnings
1413
1514from ._hooks import HookImpl
2019
2120# Need to distinguish between old- and new-style hook wrappers.
2221# 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 ()
2754
2855
2956def _raise_wrapfail (
30- wrap_controller : (
31- Generator [None , Result [object ], None ] | Generator [None , object , object ]
32- ),
57+ wrap_controller : Generator [None , object , object ],
3358 msg : str ,
3459) -> NoReturn :
35- co = wrap_controller .gi_code # type: ignore[union- attr]
60+ co = wrap_controller .gi_code # type: ignore[attr-defined ]
3661 raise RuntimeError (
3762 f"wrap_controller at { co .co_name !r} { co .co_filename } :{ co .co_firstlineno } { msg } "
3863 )
@@ -45,7 +70,7 @@ def _warn_teardown_exception(
4570 msg += f"Plugin: { hook_impl .plugin_name } , Hook: { hook_name } \n "
4671 msg += f"{ type (e ).__name__ } : { e } \n "
4772 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 )
4974
5075
5176def _multicall (
@@ -62,31 +87,26 @@ def _multicall(
6287 __tracebackhide__ = True
6388 results : list [object ] = []
6489 exception = None
65- only_new_style_wrappers = True
6690 try : # run impl and wrapper setup functions in a loop
6791 teardowns : list [Teardown ] = []
6892 try :
6993 for hook_impl in reversed (hook_impls ):
7094 try :
7195 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
7499 if argname not in caller_kwargs :
75100 raise HookCallError (
76101 f"hook call must provide argument { argname !r} "
77- )
102+ ) from e
78103
79104 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+
90110 elif hook_impl .wrapper :
91111 try :
92112 # If this cast is not valid, a type error is raised below,
@@ -106,99 +126,44 @@ def _multicall(
106126 except BaseException as exc :
107127 exception = exc
108128 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
154131 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 :
175138 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
190149 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