Repository: pytest-dev/pytest
File: src/_pytest/fixtures.py (Python)

Code before:
        for argname in self.argnames:
            fixturedef = request._get_active_fixturedef(argname)
            if not isinstance(fixturedef, PseudoFixtureDef):
                # save fixture as one to add our finalizer to, if we're not cached
                # resolves #12135
                parent_fixtures_to_add_finalizer.append(fixturedef)

        my_cache_key = self.cache_key(request)
        if self.cached_result is not None:
            cache_key = self.cached_result[1]
            # note: comparison with `==` can fail (or be expensive) for e.g.
            # numpy arrays (#6497).
            if my_cache_key is cache_key:
                if self.cached_result[2] is not None:
                    exc = self.cached_result[2]
                    raise exc
                else:
                    result = self.cached_result[0]
                    return result
            # We have a previous but differently parametrized fixture instance
            # so we need to tear it down before creating a new one.
            self.finish(request)
            assert self.cached_result is None

        finalizer = functools.partial(self.finish, request=request)
        # add finalizer to parent fixtures
        for parent_fixture in parent_fixtures_to_add_finalizer:
            parent_fixture.addfinalizer(finalizer)

        ihook = request.node.ihook
        try:
            # Setup the fixture, run the code in it, and cache the value
            # in self.cached_result
            result = ihook.pytest_fixture_setup(fixturedef=self, request=request)
        finally:
            # schedule our finalizer, even if the setup failed
            request.node.addfinalizer(finalizer)

        return result

    def cache_key(self, request: SubRequest) -> object:
        return request.param_index if not hasattr(request, "param") else request.param

    def __repr__(self) -> str:
        return f"<FixtureDef argname={self.argname!r} scope={self.scope!r} baseid={self.baseid!r}>"


def resolve_fixture_function(
    fixturedef: FixtureDef[FixtureValue], request: FixtureRequest
) -> "_FixtureFunc[FixtureValue]":
    """Get the actual callable that can be called to obtain the fixture

Review comment (suggestion):
I think the comment at the top is sufficient

```suggestion
```

Code after:
        # See #4871
        requested_fixtures_that_should_finalize_us = []
        for argname in self.argnames:
            fixturedef = request._get_active_fixturedef(argname)
            # Saves requested fixtures in a list so we later can add our finalizer
            # to them, ensuring that if a requested fixture gets torn down we get torn
            # down first. This is generally handled by SetupState, but still currently
            # needed when this fixture is not parametrized but depends on a parametrized
            # fixture.
            if not isinstance(fixturedef, PseudoFixtureDef):
                requested_fixtures_that_should_finalize_us.append(fixturedef)

        # Check for (and return) cached value/exception.
        my_cache_key = self.cache_key(request)
        if self.cached_result is not None:
            cache_key = self.cached_result[1]
            # note: comparison with `==` can fail (or be expensive) for e.g.
            # numpy arrays (#6497).
            if my_cache_key is cache_key:
                if self.cached_result[2] is not None:
                    exc = self.cached_result[2]
                    raise exc
                else:
                    result = self.cached_result[0]
                    return result
            # We have a previous but differently parametrized fixture instance
            # so we need to tear it down before creating a new one.
            self.finish(request)
            assert self.cached_result is None

        # Add finalizer to requested fixtures we saved previously.
        # We make sure to do this after checking for cached value to avoid
        # adding our finalizer multiple times. (#12135)
        finalizer = functools.partial(self.finish, request=request)
        for parent_fixture in requested_fixtures_that_should_finalize_us:
            parent_fixture.addfinalizer(finalizer)

        ihook = request.node.ihook
        try:
            # Setup the fixture, run the code in it, and cache the value
            # in self.cached_result
            result = ihook.pytest_fixture_setup(fixturedef=self, request=request)
        finally:
            # schedule our finalizer, even if the setup failed
            request.node.addfinalizer(finalizer)

        return result

    def cache_key(self, request: SubRequest) -> object:
        return getattr(request, "param", None)