| 1144 | self._finalizers.append(finalizer) |
| 1145 | |
| 1146 | def finish(self, request: SubRequest) -> None: |
| 1147 | if self.cached_result is None: |
| 1148 | # Already finished. It is assumed that finalizers cannot be added in |
| 1149 | # this state. |
| 1150 | return |
| 1151 | |
| 1152 | exceptions: list[BaseException] = [] |
| 1153 | while self._finalizers: |
| 1154 | fin = self._finalizers.pop() |
| 1155 | try: |
| 1156 | fin() |
| 1157 | except BaseException as e: |
| 1158 | exceptions.append(e) |
| 1159 | node = request.node |
| 1160 | # Even if finalization fails, we invalidate the cached fixture |
| 1161 | # value and remove all finalizers because they may be bound methods |
| 1162 | # which will keep instances alive. |
| 1163 | self.cached_result = None |
| 1164 | self._finalizers.clear() |
| 1165 | if len(exceptions) == 1: |
| 1166 | raise exceptions[0] |
| 1167 | elif len(exceptions) > 1: |
| 1168 | msg = f'errors while tearing down fixture "{self.argname}" of {node}' |
| 1169 | raise BaseExceptionGroup(msg, exceptions[::-1]) |
| 1170 | |
| 1171 | def execute(self, request: SubRequest) -> FixtureValue: |
| 1172 | """Return the value of this fixture, executing it if not cached.""" |