{"record":{"id":"99a7aee67bb83965","repo":"pytest-dev/pytest","slug":"parsefactories-requires-holder-or-node-or-obj","errorCode":null,"errorMessage":"parsefactories() requires holder or node_or_obj","messagePattern":"parsefactories\\(\\) requires holder or node_or_obj","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/_pytest/fixtures.py","lineNumber":2277,"sourceCode":"        The preferred API uses keyword-only arguments:\n        - ``holder``: The object to scan for fixtures.\n        - ``node``: The node determining fixture visibility.\n\n        Legacy positional API (translated internally):\n        - ``parsefactories(node)``: Uses node.obj as holder, node for scope.\n        - ``parsefactories(obj, nodeid)``: Uses obj as holder, nodeid string for scope.\n        \"\"\"\n        # Translate legacy API to holder/node sources of truth\n        # Either effective_node or effective_nodeid will be set, not both\n        effective_node: nodes.Node | NotSetType = NOTSET\n        effective_nodeid: str | NotSetType | None = NOTSET\n\n        if holder is not NOTSET:\n            # New API: holder and node explicitly provided\n            holderobj = holder\n            effective_node = node\n        elif node_or_obj is NOTSET:\n            raise TypeError(\"parsefactories() requires holder or node_or_obj\")\n        elif nodeid is not NOTSET:\n            # Legacy: parsefactories(obj, nodeid) - string-based scoping only.\n            warnings.warn(PARSEFACTORIES_NODEID_DEPRECATED, stacklevel=2)\n            holderobj = node_or_obj\n            effective_nodeid = nodeid\n        else:\n            # parsefactories(node) - node has .obj attribute\n            assert isinstance(node_or_obj, nodes.Node)\n            holderobj = cast(object, node_or_obj.obj)  # type: ignore[attr-defined]\n            effective_node = node_or_obj\n\n        # Avoid accessing `@property` (and other descriptors) when iterating fixtures.\n        holderobj_tp: type | types.ModuleType\n        if not safe_isclass(holderobj) and not isinstance(holderobj, types.ModuleType):\n            holderobj_tp = type(holderobj)\n        else:\n            holderobj_tp = cast(\"type | types.ModuleType\", holderobj)\n","sourceCodeStart":2259,"sourceCodeEnd":2295,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/fixtures.py#L2259-L2295","documentation":"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.","triggerScenarios":"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.","commonSituations":"Third-party plugins using the deprecated parsefactories(obj, nodeid) signature and partially migrating; monkeypatching _pytest internals; custom dynamic fixture registration code.","solutions":["Pass 'holder=obj, node=node' using the new keyword API.","If using legacy 'parsefactories(obj, nodeid)', pass obj as node_or_obj and the nodeid string as nodeid (expect a deprecation warning).","Prefer registering fixtures via @pytest.fixture in conftest.py rather than calling parsefactories manually.","Consult the docstring of parsefactories for the supported signatures before calling."],"exampleFix":"// before\nfm.parsefactories()  # missing args\n// after\nfm.parsefactories(holder=obj, node=node)","handlingStrategy":"validation","validationCode":"# Plugin authors: validate before calling parsefactories\ndef safe_parsefactories(fm, holder=None, node=None, node_or_obj=None, nodeid=None):\n    if holder is None and node_or_obj is None:\n        raise TypeError('parsefactories() requires holder or node_or_obj')\n    return fm.parsefactories(holder=holder, node=node, node_or_obj=node_or_obj, nodeid=nodeid)","typeGuard":null,"tryCatchPattern":"try:\n    fm.parsefactories(holder=obj, node=node)\nexcept TypeError as e:\n    if 'requires holder' in str(e):\n        # fallback to legacy signature\n        fm.parsefactories(obj, node.nodeid)\n    else:\n        raise","preventionTips":["Prefer registering fixtures via conftest.py over manual parsefactories.","Pin to the new keyword API when extending pytest.","Read the parsefactories docstring before calling."],"tags":["pytest","fixtures","internal-api","plugin","signature"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}