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

  1. Remove the starred/unnamed arguments and call the assertion method with explicit positional/keyword arguments so PT009 can rewrite it
  2. Leave the call as-is and add `# noqa: PT009` to suppress the diagnostic if the unittest style is intentional
  3. 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

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


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-09-05). Data as JSON: /api/errors/1a80ac3e3c819ebd. Report an issue: GitHub.