Return the value of this fixture, executing it if not cached.
(self, request: SubRequest)
| 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.""" |
| 1173 | # Ensure that the dependent fixtures requested by this fixture are loaded. |
| 1174 | # This needs to be done before checking if we have a cached value, since |
| 1175 | # if a dependent fixture has their cache invalidated, e.g. due to |
| 1176 | # parametrization, they finalize themselves and fixtures depending on it |
| 1177 | # (which will likely include this fixture) setting `self.cached_result = None`. |
| 1178 | # See #4871 |
| 1179 | requested_fixtures_that_should_finalize_us = [] |
| 1180 | for argname in self.argnames: |
| 1181 | fixturedef = request._get_active_fixturedef(argname) |
| 1182 | # Saves requested fixtures in a list so we later can add our finalizer |
| 1183 | # to them, ensuring that if a requested fixture gets torn down we get torn |
| 1184 | # down first. This is generally handled by SetupState, but still currently |
| 1185 | # needed when this fixture is not parametrized but depends on a parametrized |
| 1186 | # fixture. |
| 1187 | requested_fixtures_that_should_finalize_us.append(fixturedef) |
| 1188 | |
| 1189 | # Check for (and return) cached value/exception. |
| 1190 | if self.cached_result is not None: |
| 1191 | request_cache_key = self.cache_key(request) |
| 1192 | cache_key = self.cached_result[1] |
| 1193 | try: |
| 1194 | # Attempt to make a normal == check: this might fail for objects |
| 1195 | # which do not implement the standard comparison (like numpy arrays -- #6497). |
| 1196 | cache_hit = bool(request_cache_key == cache_key) |
| 1197 | except (ValueError, RuntimeError): |
| 1198 | # If the comparison raises, use 'is' as fallback. |
| 1199 | cache_hit = request_cache_key is cache_key |
| 1200 | |
| 1201 | if cache_hit: |
| 1202 | if self.cached_result[2] is not None: |
| 1203 | exc, exc_tb = self.cached_result[2] |
| 1204 | raise exc.with_traceback(exc_tb) |
| 1205 | else: |
| 1206 | return self.cached_result[0] |
| 1207 | # We have a previous but differently parametrized fixture instance |
| 1208 | # so we need to tear it down before creating a new one. |
| 1209 | self.finish(request) |
| 1210 | assert self.cached_result is None |
| 1211 | |
| 1212 | # Add finalizer to requested fixtures we saved previously. |
| 1213 | # We make sure to do this after checking for cached value to avoid |
| 1214 | # adding our finalizer multiple times. (#12135) |
| 1215 | finalizer = functools.partial(self.finish, request=request) |
| 1216 | for parent_fixture in requested_fixtures_that_should_finalize_us: |
| 1217 | parent_fixture.addfinalizer(finalizer) |
| 1218 | |
| 1219 | # Register the pytest_fixture_post_finalizer as the first finalizer, |
| 1220 | # which is executed last. |
| 1221 | assert not self._finalizers |
| 1222 | self.addfinalizer( |
| 1223 | lambda: request.node.ihook.pytest_fixture_post_finalizer( |
| 1224 | fixturedef=self, request=request |
| 1225 | ) |
| 1226 | ) |
| 1227 | |
| 1228 | ihook = request.node.ihook |
no test coverage detected