pandas-dev/pandas · error · SyntaxError

only a single expression is allowed

Error message

only a single expression is allowed

What it means

Raised by visit_Module() when the parsed module body does not contain exactly one expression/statement node. pandas eval/query expects a single expression; if the string parses to multiple statements (e.g., two expressions separated by a semicolon or a newline in a context that produces multiple body nodes), the visitor rejects it. This is distinct from the multi-line eval handling — it fires at the parser level for a single eval call that contains more than one top-level expression.

Solutions

  1. Evaluate one expression per call: call pd.eval twice instead of joining with ';'.
  2. For column assignments in a DataFrame context, use df.eval with newline-separated lines (which are handled as separate single expressions).
  3. Remove stray semicolons or statement separators from the expression string.

Example fix

// before
pd.eval('a + b; c + d')

// after
r1 = pd.eval('a + b')
r2 = pd.eval('c + d')
Defensive patterns

Strategy: validation

Validate before calling

if isinstance(expr, str) and ';' in expr:
    raise ValueError('eval accepts a single expression; split on ";" into separate calls')
pd.eval(expr)

Try / catch

try:
    pd.eval(expr)
except SyntaxError as e:
    if 'single expression' in str(e):
        results = [pd.eval(s) for s in expr.split(';')]
    else:
        raise

Prevention

When it happens

Trigger: Calling pd.eval('a; b') (semicolon-separated expressions in a single eval); passing a string that Python's ast.parse splits into multiple body entries unexpectedly.

Common situations: Using semicolons to combine expressions inside one eval string; building expressions by joining with ';' programmatically; misformatted strings with stray semicolons.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/9510dc65fb1627db. Report an issue: GitHub.

Appendix: source

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

        self.assigner = None

    def visit(self, node, **kwargs):
        if isinstance(node, str):
            clean = self.preparser(node)
            try:
                node = ast.fix_missing_locations(ast.parse(clean))
            except SyntaxError as e:
                if any(iskeyword(x) for x in clean.split()):
                    e.msg = "Python keyword not valid identifier in numexpr query"
                raise e

        method = f"visit_{type(node).__name__}"
        visitor = getattr(self, method)
        return visitor(node, **kwargs)

    def visit_Module(self, node, **kwargs):
        if len(node.body) != 1:
            raise SyntaxError("only a single expression is allowed")
        expr = node.body[0]
        return self.visit(expr, **kwargs)

    def visit_Expr(self, node, **kwargs):
        return self.visit(node.value, **kwargs)

    def _rewrite_membership_op(self, node, left, right):
        # the kind of the operator (is actually an instance)
        op_instance = node.op
        op_type = type(op_instance)

        # must be two terms and the comparison operator must be ==/!=/in/not in
        if is_term(left) and is_term(right) and op_type in self.rewrite_map:
            left_list, right_list = map(_is_list, (left, right))
            left_str, right_str = map(_is_str, (left, right))

            # if there are any strings or lists in the expression
            if left_list or right_list or left_str or right_str:

View on GitHub (pinned to 3b7651241d)