pandas-dev/pandas · error · SyntaxError

can only assign a single expression

Error message

can only assign a single expression

What it means

visit_Assign (expr.py:613) handles exactly one target: len(node.targets) != 1 raises. Python's AST gives chained assignment 'a = b = 1' two targets and tuple-target assignment 'a, b = ...' a single Tuple target, both of which are rejected. The eval grammar intentionally supports only 'name = expr'.

Source

Thrown at pandas/core/computation/expr.py:624

            upper = self.visit(upper).value
        step = node.step
        if step is not None:
            step = self.visit(step).value

        return slice(lower, upper, step)

    def visit_Assign(self, node, **kwargs):
        """
        support a single assignment node, like

        c = a + b

        set the assigner at the top level, must be a Name node which
        might or might not exist in the resolvers

        """
        if len(node.targets) != 1:
            raise SyntaxError("can only assign a single expression")
        if not isinstance(node.targets[0], ast.Name):
            raise SyntaxError("left hand side of an assignment must be a single name")
        if self.env.target is None:
            raise ValueError("cannot assign without a target object")

        try:
            assigner = self.visit(node.targets[0], **kwargs)
        except UndefinedVariableError:
            assigner = node.targets[0].id

        self.assigner = getattr(assigner, "name", assigner)
        if self.assigner is None:
            raise SyntaxError(
                "left hand side of an assignment must be a single resolvable name"
            )

        return self.visit(node.value, **kwargs)

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Split into separate single-target assignments, one per line: df.eval('a = 1\nb = 2').
  2. Compute the values in plain Python and assign columns directly.
  3. Use a multi-line df.eval string where each line is a single assignment.

Example fix

// before
df.eval('a = b = 1')
// after
df.eval('a = 1\nb = 1')
Defensive patterns

Strategy: validation

Validate before calling

import ast

def validate_single_target_assignment(expr: str) -> None:
    for stmt in ast.parse(expr, mode='exec').body:
        if isinstance(stmt, ast.Assign) and len(stmt.targets) != 1:
            raise SyntaxError(
                f'only single-target assignment supported; split: {expr!r}'
            )

validate_single_target_assignment(expr)

Type guard

import ast

def uses_only_single_assignments(expr: str) -> bool:
    return all(
        not isinstance(s, ast.Assign) or len(s.targets) == 1
        for s in ast.parse(expr, mode='exec').body
    )

Try / catch

try:
    df.eval(expr)
except SyntaxError as e:
    if 'single expression' in str(e) and '=' in expr:
        # rewrite 'a = b = v' into 'a = v\nb = v'
        df.eval(expr.replace('=', '=...').replace('=', '='))
    raise

Prevention

When it happens

Trigger: df.eval('a = b = 1'), df.eval('a, b = (1, 2)'), or any assignment form Python parses into multiple targets.

Common situations: Trying to initialize several columns at once. Porting Python chained assignment idioms into eval. Templating systems that emit multi-target assignments.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/c21a70eddc9a268a. Report an issue: GitHub.