|
3 | 3 | """ |
4 | 4 | from __future__ import annotations |
5 | 5 |
|
| 6 | +import warnings |
6 | 7 | from typing import cast |
7 | 8 | from typing import Generator |
8 | 9 | from typing import Mapping |
| 10 | +from typing import NoReturn |
9 | 11 | from typing import Sequence |
10 | 12 | from typing import Tuple |
11 | 13 | from typing import Union |
12 | 14 |
|
13 | 15 | from ._hooks import HookImpl |
14 | | -from ._result import _raise_wrapfail |
15 | 16 | from ._result import HookCallError |
16 | 17 | from ._result import Result |
| 18 | +from ._warnings import PluggyTeardownRaisedWarning |
17 | 19 |
|
18 | 20 |
|
19 | 21 | # Need to distinguish between old- and new-style hook wrappers. |
20 | | -# Wrapping one a singleton tuple is the fastest type-safe way I found to do it. |
| 22 | +# Wrapping with a tuple is the fastest type-safe way I found to do it. |
21 | 23 | Teardown = Union[ |
22 | | - Tuple[Generator[None, Result[object], None]], |
| 24 | + Tuple[Generator[None, Result[object], None], HookImpl], |
23 | 25 | Generator[None, object, object], |
24 | 26 | ] |
25 | 27 |
|
26 | 28 |
|
| 29 | +def _raise_wrapfail( |
| 30 | + wrap_controller: ( |
| 31 | + Generator[None, Result[object], None] | Generator[None, object, object] |
| 32 | + ), |
| 33 | + msg: str, |
| 34 | +) -> NoReturn: |
| 35 | + co = wrap_controller.gi_code |
| 36 | + raise RuntimeError( |
| 37 | + "wrap_controller at %r %s:%d %s" |
| 38 | + % (co.co_name, co.co_filename, co.co_firstlineno, msg) |
| 39 | + ) |
| 40 | + |
| 41 | + |
| 42 | +def _warn_teardown_exception( |
| 43 | + hook_name: str, hook_impl: HookImpl, e: BaseException |
| 44 | +) -> None: |
| 45 | + msg = "A plugin raised an exception during an old-style hookwrapper teardown.\n" |
| 46 | + msg += f"Plugin: {hook_impl.plugin_name}, Hook: {hook_name}\n" |
| 47 | + msg += f"{type(e).__name__}: {e}\n" |
| 48 | + msg += "For more information see https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluggyTeardownRaisedWarning" # noqa: E501 |
| 49 | + warnings.warn(PluggyTeardownRaisedWarning(msg), stacklevel=5) |
| 50 | + |
| 51 | + |
27 | 52 | def _multicall( |
28 | 53 | hook_name: str, |
29 | 54 | hook_impls: Sequence[HookImpl], |
@@ -60,7 +85,7 @@ def _multicall( |
60 | 85 | res = hook_impl.function(*args) |
61 | 86 | wrapper_gen = cast(Generator[None, Result[object], None], res) |
62 | 87 | next(wrapper_gen) # first yield |
63 | | - teardowns.append((wrapper_gen,)) |
| 88 | + teardowns.append((wrapper_gen, hook_impl)) |
64 | 89 | except StopIteration: |
65 | 90 | _raise_wrapfail(wrapper_gen, "did not yield") |
66 | 91 | elif hook_impl.wrapper: |
@@ -128,9 +153,13 @@ def _multicall( |
128 | 153 | if isinstance(teardown, tuple): |
129 | 154 | try: |
130 | 155 | teardown[0].send(outcome) |
131 | | - _raise_wrapfail(teardown[0], "has second yield") |
132 | 156 | except StopIteration: |
133 | 157 | pass |
| 158 | + except BaseException as e: |
| 159 | + _warn_teardown_exception(hook_name, teardown[1], e) |
| 160 | + raise |
| 161 | + else: |
| 162 | + _raise_wrapfail(teardown[0], "has second yield") |
134 | 163 | else: |
135 | 164 | try: |
136 | 165 | if outcome._exception is not None: |
|
0 commit comments