{"record":{"id":"1a80ac3e3c819ebd","repo":"astral-sh/ruff","slug":"variable-length-arguments-are-not-supported","errorCode":null,"errorMessage":"Variable-length arguments are not supported","messagePattern":"Variable-length arguments are not supported","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs","lineNumber":237,"sourceCode":"            UnittestAssert::Underscore => &[\"expr\", \"msg\"],\n            UnittestAssert::FailIf => &[\"expr\", \"msg\"],\n            UnittestAssert::FailIfAlmostEqual => &[\"first\", \"second\", \"msg\"],\n            UnittestAssert::FailIfEqual => &[\"first\", \"second\", \"msg\"],\n            UnittestAssert::FailUnless => &[\"expr\", \"msg\"],\n            UnittestAssert::FailUnlessAlmostEqual => &[\"first\", \"second\", \"places\", \"msg\", \"delta\"],\n            UnittestAssert::FailUnlessEqual => &[\"first\", \"second\", \"places\", \"msg\", \"delta\"],\n        }\n    }\n\n    /// Create a map from argument name to value.\n    fn args_map<'a>(\n        &'a self,\n        args: &'a [Expr],\n        keywords: &'a [Keyword],\n    ) -> Result<FxHashMap<&'a str, &'a Expr>> {\n        // If we have variable-length arguments, abort.\n        if args.iter().any(Expr::is_starred_expr) || keywords.iter().any(|kw| kw.arg.is_none()) {\n            bail!(\"Variable-length arguments are not supported\");\n        }\n\n        let arg_spec = self.arg_spec();\n\n        // If any of the keyword arguments are not in the argument spec, abort.\n        if keywords.iter().any(|kw| {\n            kw.arg\n                .as_ref()\n                .is_some_and(|kwarg_name| !arg_spec.contains(&kwarg_name.as_str()))\n        }) {\n            bail!(\"Unknown keyword argument\");\n        }\n\n        // Generate a map from argument name to value.\n        let mut args_map: FxHashMap<&str, &Expr> =\n            FxHashMap::with_capacity_and_hasher(args.len() + keywords.len(), FxBuildHasher);\n\n        // Process positional arguments.","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs#L219-L255","documentation":"This is an internal abort raised by `args_map` in the flake8-pytest-style unittest-assert rewriter (PT005/PT006/PT009-style fix generation). The function builds a name->expression map for an `assertX(...)` call so it can be rewritten as a plain `assert`; if the call uses `*args` or `**kwargs`-style syntax (`Expr::is_starred_expr` or a keyword with no name), the arguments cannot be mapped to the call's argument spec, so the fix bails out. The diagnostic is still reported; only the automatic fix is skipped.","triggerScenarios":"Running `ruff check --fix` on a unittest-style assertion call that spreads variable-length arguments, e.g. `self.assertEqual(*expected)` or `self.assertRaisesRegex(E, 're', **kw)`, with rule PT009 (unittest-assert) fix enabled.","commonSituations":"Test suites migrating from unittest to pytest style where helper wrappers forward `*args`/`**kwargs` into assertion methods; metaprogrammed or generated tests that splat arguments.","solutions":["Remove the starred/unnamed arguments and call the assertion method with explicit positional/keyword arguments so PT009 can rewrite it","Leave the call as-is and add `# noqa: PT009` to suppress the diagnostic if the unittest style is intentional","Configure flake8-pytest-style fixture/eval settings or disable the fix (keep the diagnostic unfixed) via `lint.fixable = [...]`"],"exampleFix":"// before\nself.assertEqual(*values)\n// after\nself.assertEqual(values[0], values[1])","handlingStrategy":"validation","validationCode":"import ast\n\ndef pt009_fixable(call: ast.Call) -> bool:\n    has_star_args = any(isinstance(a, ast.Starred) for a in call.args)\n    has_kwnameless = any(kw.arg is None for kw in call.keywords)\n    return not (has_star_args or has_kwnameless)\n\n# run ruff --fix only when pt009_fixable(node)","typeGuard":"def is_plain_call(node: ast.AST) -> bool:\n    return isinstance(node, ast.Call) and pt009_fixable(node)","tryCatchPattern":null,"preventionTips":["Call unittest assertion methods with explicit positional and keyword arguments, never *args/**kwargs","Run ruff --fix on a branch and review `--diff` output before applying to the whole repo","Check the PT rules' supported-method list before relying on automated unittest-to-pytest rewrites"],"tags":["ruff","flake8-pytest-style","autofix","unittest"],"backgroundTag":"linter-autofix-skipped","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}