{"record":{"id":"31d2059140956d23","repo":"pytest-dev/pytest","slug":"plugins-may-be-specified-as-a-sequence-or-a-se","errorCode":null,"errorMessage":"Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: {specs!r}","messagePattern":"Plugins may be specified as a sequence or a ','-separated string of plugin names\\. Got: (.+?)","errorType":"exception","errorClass":"UsageError","httpStatus":null,"severity":"error","filePath":"src/_pytest/config/__init__.py","lineNumber":953,"sourceCode":"\n\ndef _get_plugin_specs_as_list(\n    specs: types.ModuleType | str | Sequence[str] | None,\n) -> list[str]:\n    \"\"\"Parse a plugins specification into a list of plugin names.\"\"\"\n    # None means empty.\n    if specs is None:\n        return []\n    # Workaround for #3899 - a submodule which happens to be called \"pytest_plugins\".\n    if isinstance(specs, types.ModuleType):\n        return []\n    # Comma-separated list.\n    if isinstance(specs, str):\n        return specs.split(\",\") if specs else []\n    # Direct specification.\n    if isinstance(specs, collections.abc.Sequence):\n        return list(specs)\n    raise UsageError(\n        f\"Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: {specs!r}\"\n    )\n\n\ndef _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]:\n    \"\"\"Given an iterable of file names in a source distribution, return the \"names\" that should\n    be marked for assertion rewrite.\n\n    For example the package \"pytest_mock/__init__.py\" should be added as \"pytest_mock\" in\n    the assertion rewrite mechanism.\n\n    This function has to deal with dist-info based distributions and egg based distributions\n    (which are still very much in use for \"editable\" installs).\n\n    Here are the file names as seen in a dist-info based distribution:\n\n        pytest_mock/__init__.py\n        pytest_mock/_version.py","sourceCodeStart":935,"sourceCodeEnd":971,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/config/__init__.py#L935-L971","documentation":"Raised by _get_plugin_specs_as_list() (src/_pytest/config/__init__.py:953) when the pytest_plugins variable in a conftest.py is not None, not a str, not a Sequence, and not a module (config/__init__.py:942-952 cover those cases). The final raise at capture.py:953-955 surfaces the bad value via UsageError so the user sees a clear message rather than a confusing AttributeError later.","triggerScenarios":"Setting `pytest_plugins = 42` or `pytest_plugins = {'name': 'plugin'}` (a dict) in a conftest.py. None, str, list, tuple, and module types are accepted; anything else falls through to config/__init__.py:953.","commonSituations":"Typos in conftest.py; copying a dict-style config from another tool; assigning the result of a function call that returns a non-sequence; using a set literal `{...}` instead of a tuple `(...)`.","solutions":["Make pytest_plugins a string or a tuple/list of strings: `pytest_plugins = ('a', 'b')`","If you must compute it dynamically, ensure the expression yields str or Sequence[str]","Use None or omit the variable to disable plugin specs entirely"],"exampleFix":"// before\n# conftest.py\npytest_plugins = {'foo': 'plugin_foo'}\n// after\npytest_plugins = ('plugin_foo',)","handlingStrategy":"type-guard","validationCode":"import collections.abc, types\nok = specs is None or isinstance(specs, (str, collections.abc.Sequence, types.ModuleType))\nif not ok:\n    raise TypeError('pytest_plugins must be None/str/Sequence/module')","typeGuard":"import collections.abc, types\ndef valid_plugin_specs(s) -> bool:\n    return s is None or isinstance(s, (str, collections.abc.Sequence, types.ModuleType))","tryCatchPattern":null,"preventionTips":["Use a tuple literal for pytest_plugins in conftest.py","Avoid dict/set/dict-values - only str, tuple, list, or None","Lint conftest.py for unusual pytest_plugins assignments"],"tags":["plugin","conftest","config","validation"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}