|
7 | 7 | import inspect
|
8 | 8 | import os
|
9 | 9 | from pathlib import Path
|
| 10 | +import sys |
10 | 11 | from typing import AbstractSet
|
11 | 12 | from typing import Any
|
12 | 13 | from typing import Callable
|
|
67 | 68 | from _pytest.scope import Scope
|
68 | 69 |
|
69 | 70 |
|
| 71 | +if sys.version_info[:2] < (3, 11): |
| 72 | + from exceptiongroup import BaseExceptionGroup |
| 73 | + |
| 74 | + |
70 | 75 | if TYPE_CHECKING:
|
71 | 76 | from typing import Deque
|
72 | 77 |
|
@@ -1017,27 +1022,25 @@ def addfinalizer(self, finalizer: Callable[[], object]) -> None:
|
1017 | 1022 | self._finalizers.append(finalizer)
|
1018 | 1023 |
|
1019 | 1024 | def finish(self, request: SubRequest) -> None:
|
1020 |
| - exc = None |
1021 |
| - try: |
1022 |
| - while self._finalizers: |
1023 |
| - try: |
1024 |
| - func = self._finalizers.pop() |
1025 |
| - func() |
1026 |
| - except BaseException as e: |
1027 |
| - # XXX Only first exception will be seen by user, |
1028 |
| - # ideally all should be reported. |
1029 |
| - if exc is None: |
1030 |
| - exc = e |
1031 |
| - if exc: |
1032 |
| - raise exc |
1033 |
| - finally: |
1034 |
| - ihook = request.node.ihook |
1035 |
| - ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request) |
1036 |
| - # Even if finalization fails, we invalidate the cached fixture |
1037 |
| - # value and remove all finalizers because they may be bound methods |
1038 |
| - # which will keep instances alive. |
1039 |
| - self.cached_result = None |
1040 |
| - self._finalizers.clear() |
| 1025 | + exceptions: List[BaseException] = [] |
| 1026 | + while self._finalizers: |
| 1027 | + fin = self._finalizers.pop() |
| 1028 | + try: |
| 1029 | + fin() |
| 1030 | + except BaseException as e: |
| 1031 | + exceptions.append(e) |
| 1032 | + node = request.node |
| 1033 | + node.ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request) |
| 1034 | + # Even if finalization fails, we invalidate the cached fixture |
| 1035 | + # value and remove all finalizers because they may be bound methods |
| 1036 | + # which will keep instances alive. |
| 1037 | + self.cached_result = None |
| 1038 | + self._finalizers.clear() |
| 1039 | + if len(exceptions) == 1: |
| 1040 | + raise exceptions[0] |
| 1041 | + elif len(exceptions) > 1: |
| 1042 | + msg = f'errors while tearing down fixture "{self.argname}" of {node}' |
| 1043 | + raise BaseExceptionGroup(msg, exceptions[::-1]) |
1041 | 1044 |
|
1042 | 1045 | def execute(self, request: SubRequest) -> FixtureValue:
|
1043 | 1046 | # Get required arguments and register our own finish()
|
|
0 commit comments