|
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