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

Code before:
        # `fixturedefs` is sorted from furthest to closest, so use negative
        # indexing to go in reverse.
        index = -1
        for request in self._iter_chain():
            if request.fixturename == argname:
                index -= 1
        # If already consumed all of the available levels, fail.
        if -index > len(fixturedefs):
            raise FixtureLookupError(argname, self)
        fixturedef = fixturedefs[index]

        # Check for attempted use of an async fixture by a sync test
        # `self.scope` here is not the scope of the requested fixture, but the scope of
        # the requester.
        if (
            self.scope == "function"
            and not inspect.iscoroutinefunction(self._pyfuncitem.obj)
            and (
                inspect.iscoroutinefunction(fixturedef.func)
                or inspect.isasyncgenfunction(fixturedef.func)
            )
        ):
            if fixturedef._autouse:
                warnings.warn(
                    PytestRemovedIn9Warning(
                        "Sync test requested an async fixture with autouse=True. "
                        "If you intended to use the fixture you may want to make the "
                        "test asynchronous. If you did not intend to use it you should "
                        "restructure your test setup. "
                        "This will turn into an error in pytest 9."
                    ),
                    stacklevel=3,
                )
            else:
                raise FixtureLookupError(
                    argname,
                    self,
                    (
                        "ERROR: Sync test requested async fixture. "
                        "You may want to make the test asynchronous and run it with "
                        "a suitable async framework test plugin."
                    ),
                )

        # Prepare a SubRequest object for calling the fixture.
        try:
            callspec = self._pyfuncitem.callspec
        except AttributeError:
            callspec = None
        if callspec is not None and argname in callspec.params:
            param = callspec.params[argname]

Review comment (suggestion):
Add the test name to "Sync test '{name}'" and the fixture name to "async fixture '{name}'" in the phrase here to help users understand the problem better.

We also should add an entry to "deprecations", with the rationale for this and guiding users on how to update their code (installing plugins, changing the `async` fixture, etc).

Code after:
        # `fixturedefs` is sorted from furthest to closest, so use negative
        # indexing to go in reverse.
        index = -1
        for request in self._iter_chain():
            if request.fixturename == argname:
                index -= 1
        # If already consumed all of the available levels, fail.
        if -index > len(fixturedefs):
            raise FixtureLookupError(argname, self)
        fixturedef = fixturedefs[index]

        # Prepare a SubRequest object for calling the fixture.
        try:
            callspec = self._pyfuncitem.callspec
        except AttributeError:
            callspec = None
        if callspec is not None and argname in callspec.params:
            param = callspec.params[argname]
            param_index = callspec.indices[argname]
            # The parametrize invocation scope overrides the fixture's scope.
            scope = callspec._arg2scope[argname]
        else:
            param = NOTSET
            param_index = 0
            scope = fixturedef._scope
            self._check_fixturedef_without_param(fixturedef)
        self._check_scope(fixturedef, scope)
        subrequest = SubRequest(
            self, scope, param, param_index, fixturedef, _ispytest=True
        )

        # Make sure the fixture value is cached, running it if it isn't
        fixturedef.execute(request=subrequest)

        self._fixture_defs[argname] = fixturedef
        return fixturedef

    def _check_fixturedef_without_param(self, fixturedef: FixtureDef[object]) -> None:
        """Check that this request is allowed to execute this fixturedef without
        a param."""
        funcitem = self._pyfuncitem
        has_params = fixturedef.params is not None
        fixtures_not_supported = getattr(funcitem, "nofuncargs", False)
        if has_params and fixtures_not_supported:
            msg = (
                f"{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n"
                f"Node id: {funcitem.nodeid}\n"
                f"Function type: {type(funcitem).__name__}"
            )
            fail(msg, pytrace=False)
        if has_params: