{"record":{"id":"c21a70eddc9a268a","repo":"pandas-dev/pandas","slug":"can-only-assign-a-single-expression","errorCode":null,"errorMessage":"can only assign a single expression","messagePattern":"can only assign a single expression","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"pandas/core/computation/expr.py","lineNumber":624,"sourceCode":"            upper = self.visit(upper).value\n        step = node.step\n        if step is not None:\n            step = self.visit(step).value\n\n        return slice(lower, upper, step)\n\n    def visit_Assign(self, node, **kwargs):\n        \"\"\"\n        support a single assignment node, like\n\n        c = a + b\n\n        set the assigner at the top level, must be a Name node which\n        might or might not exist in the resolvers\n\n        \"\"\"\n        if len(node.targets) != 1:\n            raise SyntaxError(\"can only assign a single expression\")\n        if not isinstance(node.targets[0], ast.Name):\n            raise SyntaxError(\"left hand side of an assignment must be a single name\")\n        if self.env.target is None:\n            raise ValueError(\"cannot assign without a target object\")\n\n        try:\n            assigner = self.visit(node.targets[0], **kwargs)\n        except UndefinedVariableError:\n            assigner = node.targets[0].id\n\n        self.assigner = getattr(assigner, \"name\", assigner)\n        if self.assigner is None:\n            raise SyntaxError(\n                \"left hand side of an assignment must be a single resolvable name\"\n            )\n\n        return self.visit(node.value, **kwargs)\n","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/computation/expr.py#L606-L642","documentation":"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'.","triggerScenarios":"df.eval('a = b = 1'), df.eval('a, b = (1, 2)'), or any assignment form Python parses into multiple targets.","commonSituations":"Trying to initialize several columns at once. Porting Python chained assignment idioms into eval. Templating systems that emit multi-target assignments.","solutions":["Split into separate single-target assignments, one per line: df.eval('a = 1\\nb = 2').","Compute the values in plain Python and assign columns directly.","Use a multi-line df.eval string where each line is a single assignment."],"exampleFix":"// before\ndf.eval('a = b = 1')\n// after\ndf.eval('a = 1\\nb = 1')","handlingStrategy":"validation","validationCode":"import ast\n\ndef validate_single_target_assignment(expr: str) -> None:\n    for stmt in ast.parse(expr, mode='exec').body:\n        if isinstance(stmt, ast.Assign) and len(stmt.targets) != 1:\n            raise SyntaxError(\n                f'only single-target assignment supported; split: {expr!r}'\n            )\n\nvalidate_single_target_assignment(expr)","typeGuard":"import ast\n\ndef uses_only_single_assignments(expr: str) -> bool:\n    return all(\n        not isinstance(s, ast.Assign) or len(s.targets) == 1\n        for s in ast.parse(expr, mode='exec').body\n    )","tryCatchPattern":"try:\n    df.eval(expr)\nexcept SyntaxError as e:\n    if 'single expression' in str(e) and '=' in expr:\n        # rewrite 'a = b = v' into 'a = v\\nb = v'\n        df.eval(expr.replace('=', '=...').replace('=', '='))\n    raise","preventionTips":["Write one assignment target per line in multi-line eval strings.","Never use chained 'a = b = v' or tuple unpacking in eval.","Validate programmatically generated assignments with ast.parse."],"tags":["pandas","eval","assignment","syntax"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}