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

Code before:
            if item in items_done or item in no_argkey_group:
                continue
            argkeys = dict.fromkeys(
                (k for k in scoped_argkeys_cache.get(item, []) if k not in ignore), None
            )
            if not argkeys:
                no_argkey_group[item] = None
            else:
                slicing_argkey, _ = argkeys.popitem()
                # We don't have to remove relevant items from later in the
                # deque because they'll just be ignored.
                matching_items = [
                    i for i in scoped_items_by_argkey[slicing_argkey] if i in items
                ]
                for i in reversed(matching_items):
                    fix_cache_order(i, argkeys_cache, items_by_argkey)
                    items_deque.appendleft(i)
                break
        if no_argkey_group:
            no_argkey_group = reorder_items_atscope(
                no_argkey_group, argkeys_cache, items_by_argkey, scope.next_lower()
            )
            for item in no_argkey_group:
                items_done[item] = None
        ignore.add(slicing_argkey)
    return items_done


@dataclasses.dataclass(frozen=True)
class FuncFixtureInfo:
    """Fixture-related information for a fixture-requesting item (e.g. test
    function).

    This is used to examine the fixtures which an item requests statically
    (known during collection). This includes autouse fixtures, fixtures
    requested by the `usefixtures` marker, fixtures requested in the function
    parameters, and the transitive closure of these.

    An item may also request fixtures dynamically (using `request.getfixturevalue`);
    these are not reflected here.
    """

    __slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs")

    # Fixture names that the item requests directly by function parameters.
    argnames: Tuple[str, ...]
    # Fixture names that the item immediately requires. These include
    # argnames + fixture names specified via usefixtures and via autouse=True in
    # fixture definitions.
    initialnames: Tuple[str, ...]
    # The transitive closure of the fixture names that the item requires.

Review comment (bug):
@bluetech I've hit some corner case on PyPy where `move_to_end()` seems to be raising a `KeyError` and crashing the item collection entirely...

Code after:
        no_argkey_items: OrderedSet[nodes.Item] = {}
        slicing_argkey = None
        while items_deque:
            item = items_deque.popleft()
            if item in items_done or item in no_argkey_items:
                continue
            argkeys = dict.fromkeys(
                k for k in scoped_argkeys_by_item.get(item, ()) if k not in ignore
            )
            if not argkeys:
                no_argkey_items[item] = None
            else:
                slicing_argkey, _ = argkeys.popitem()
                # We don't have to remove relevant items from later in the
                # deque because they'll just be ignored.
                matching_items = [
                    i for i in scoped_items_by_argkey[slicing_argkey] if i in items
                ]
                for i in reversed(matching_items):
                    items_deque.appendleft(i)
                    # Fix items_by_argkey order.
                    for other_scope in HIGH_SCOPES:
                        other_scoped_items_by_argkey = items_by_argkey[other_scope]
                        for argkey in argkeys_by_item[other_scope].get(i, ()):
                            other_scoped_items_by_argkey[argkey][i] = None
                            other_scoped_items_by_argkey[argkey].move_to_end(
                                i, last=False
                            )
                break
        if no_argkey_items:
            reordered_no_argkey_items = reorder_items_atscope(
                no_argkey_items, argkeys_by_item, items_by_argkey, scope.next_lower()
            )
            items_done.update(reordered_no_argkey_items)
        if slicing_argkey is not None:
            ignore.add(slicing_argkey)
    return items_done


@dataclasses.dataclass(frozen=True)
class FuncFixtureInfo:
    """Fixture-related information for a fixture-requesting item (e.g. test
    function).

    This is used to examine the fixtures which an item requests statically
    (known during collection). This includes autouse fixtures, fixtures
    requested by the `usefixtures` marker, fixtures requested in the function
    parameters, and the transitive closure of these.

    An item may also request fixtures dynamically (using `request.getfixturevalue`);
    these are not reflected here.