astral-sh/ruff · info
Variable-length arguments are not supported
Error message
Variable-length arguments are not supported
What it means
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.
Source
Thrown at crates/ruff_linter/src/rules/flake8_pytest_style/rules/unittest_assert.rs:237
UnittestAssert::Underscore => &["expr", "msg"],
UnittestAssert::FailIf => &["expr", "msg"],
UnittestAssert::FailIfAlmostEqual => &["first", "second", "msg"],
UnittestAssert::FailIfEqual => &["first", "second", "msg"],
UnittestAssert::FailUnless => &["expr", "msg"],
UnittestAssert::FailUnlessAlmostEqual => &["first", "second", "places", "msg", "delta"],
UnittestAssert::FailUnlessEqual => &["first", "second", "places", "msg", "delta"],
}
}
/// Create a map from argument name to value.
fn args_map<'a>(
&'a self,
args: &'a [Expr],
keywords: &'a [Keyword],
) -> Result<FxHashMap<&'a str, &'a Expr>> {
// If we have variable-length arguments, abort.
if args.iter().any(Expr::is_starred_expr) || keywords.iter().any(|kw| kw.arg.is_none()) {
bail!("Variable-length arguments are not supported");
}
let arg_spec = self.arg_spec();
// If any of the keyword arguments are not in the argument spec, abort.
if keywords.iter().any(|kw| {
kw.arg
.as_ref()
.is_some_and(|kwarg_name| !arg_spec.contains(&kwarg_name.as_str()))
}) {
bail!("Unknown keyword argument");
}
// Generate a map from argument name to value.
let mut args_map: FxHashMap<&str, &Expr> =
FxHashMap::with_capacity_and_hasher(args.len() + keywords.len(), FxBuildHasher);
// Process positional arguments.View on GitHub (pinned to 15f3fe6b15)
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 = [...]`
Example fix
// before self.assertEqual(*values) // after self.assertEqual(values[0], values[1])
Defensive patterns
Strategy: validation
Validate before calling
import ast
def pt009_fixable(call: ast.Call) -> bool:
has_star_args = any(isinstance(a, ast.Starred) for a in call.args)
has_kwnameless = any(kw.arg is None for kw in call.keywords)
return not (has_star_args or has_kwnameless)
# run ruff --fix only when pt009_fixable(node) Type guard
def is_plain_call(node: ast.AST) -> bool:
return isinstance(node, ast.Call) and pt009_fixable(node) Prevention
- 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
When it happens
Trigger: 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.
Common situations: Test suites migrating from unittest to pytest style where helper wrappers forward `*args`/`**kwargs` into assertion methods; metaprogrammed or generated tests that splat arguments.
Related errors
- Unknown keyword argument
- Cannot fix `{self}`
- Missing argument `expr`
- Failed to collapse `with`: {err}
- Unable to fix multiline statement
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-09-05).
Data as JSON: /api/errors/1a80ac3e3c819ebd.
Report an issue: GitHub.