pytest-dev/pytest · error · TypeError

parsefactories() requires holder or node_or_obj

Error message

parsefactories() requires holder or node_or_obj

What it means

FixtureManager.parsefactories (fixtures.py:2277) requires either the new 'holder' parameter or the legacy 'node_or_obj' parameter to identify the object holding fixture factories. If both are NOTSET, it raises TypeError immediately. This is an internal/extension-author API: regular test code never calls it directly.

Source

Thrown at src/_pytest/fixtures.py:2277

        The preferred API uses keyword-only arguments:
        - ``holder``: The object to scan for fixtures.
        - ``node``: The node determining fixture visibility.

        Legacy positional API (translated internally):
        - ``parsefactories(node)``: Uses node.obj as holder, node for scope.
        - ``parsefactories(obj, nodeid)``: Uses obj as holder, nodeid string for scope.
        """
        # Translate legacy API to holder/node sources of truth
        # Either effective_node or effective_nodeid will be set, not both
        effective_node: nodes.Node | NotSetType = NOTSET
        effective_nodeid: str | NotSetType | None = NOTSET

        if holder is not NOTSET:
            # New API: holder and node explicitly provided
            holderobj = holder
            effective_node = node
        elif node_or_obj is NOTSET:
            raise TypeError("parsefactories() requires holder or node_or_obj")
        elif nodeid is not NOTSET:
            # Legacy: parsefactories(obj, nodeid) - string-based scoping only.
            warnings.warn(PARSEFACTORIES_NODEID_DEPRECATED, stacklevel=2)
            holderobj = node_or_obj
            effective_nodeid = nodeid
        else:
            # parsefactories(node) - node has .obj attribute
            assert isinstance(node_or_obj, nodes.Node)
            holderobj = cast(object, node_or_obj.obj)  # type: ignore[attr-defined]
            effective_node = node_or_obj

        # Avoid accessing `@property` (and other descriptors) when iterating fixtures.
        holderobj_tp: type | types.ModuleType
        if not safe_isclass(holderobj) and not isinstance(holderobj, types.ModuleType):
            holderobj_tp = type(holderobj)
        else:
            holderobj_tp = cast("type | types.ModuleType", holderobj)

View on GitHub (pinned to 0d6fbdeffa)

Solutions

  1. Pass 'holder=obj, node=node' using the new keyword API.
  2. If using legacy 'parsefactories(obj, nodeid)', pass obj as node_or_obj and the nodeid string as nodeid (expect a deprecation warning).
  3. Prefer registering fixtures via @pytest.fixture in conftest.py rather than calling parsefactories manually.
  4. Consult the docstring of parsefactories for the supported signatures before calling.

Example fix

// before
fm.parsefactories()  # missing args
// after
fm.parsefactories(holder=obj, node=node)
Defensive patterns

Strategy: validation

Validate before calling

# Plugin authors: validate before calling parsefactories
def safe_parsefactories(fm, holder=None, node=None, node_or_obj=None, nodeid=None):
    if holder is None and node_or_obj is None:
        raise TypeError('parsefactories() requires holder or node_or_obj')
    return fm.parsefactories(holder=holder, node=node, node_or_obj=node_or_obj, nodeid=nodeid)

Try / catch

try:
    fm.parsefactories(holder=obj, node=node)
except TypeError as e:
    if 'requires holder' in str(e):
        # fallback to legacy signature
        fm.parsefactories(obj, node.nodeid)
    else:
        raise

Prevention

When it happens

Trigger: A plugin or conftest calling FixtureManager.parsefactories() with neither holder nor node_or_obj; passing only nodeid without obj under the legacy signature; migrating a custom collection hook that previously relied on positional args.

Common situations: Third-party plugins using the deprecated parsefactories(obj, nodeid) signature and partially migrating; monkeypatching _pytest internals; custom dynamic fixture registration code.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/99a7aee67bb83965. Report an issue: GitHub.