pandas-dev/pandas · error · SyntaxError

The '@' prefix is not allowed in top-level eval calls. pleas

Error message

The '@' prefix is not allowed in top-level eval calls.
please refer to your variables by name without the '@' prefix.

What it means

The '@' prefix resolves caller locals by walking up the call stack inside ensure_scope. A bare top-level pd.eval call (level=0, at_top_of_stack) has no enclosing DataFrame method frame to harvest locals from, so pandas refuses rather than silently resolving to the wrong scope. The fix the message points to is to reference variables by name (relying on local_dict/globals) or to use a DataFrame.eval/query call whose frame provides the scope.

Source

Thrown at pandas/core/computation/eval.py:175

    return s


def _check_for_locals(expr: str, stack_level: int, parser: str) -> None:
    at_top_of_stack = stack_level == 0
    not_pandas_parser = parser != "pandas"

    if not_pandas_parser:
        msg = "The '@' prefix is only supported by the pandas parser"
    elif at_top_of_stack:
        msg = (
            "The '@' prefix is not allowed in top-level eval calls.\n"
            "please refer to your variables by name without the '@' prefix."
        )

    if at_top_of_stack or not_pandas_parser:
        for toknum, tokval in tokenize_string(expr):
            if toknum == tokenize.OP and tokval == "@":
                raise SyntaxError(msg)


@set_module("pandas")
def eval(
    expr: str | BinOp,  # we leave BinOp out of the docstr bc it isn't for users
    parser: str = "pandas",
    engine: str | None = None,
    local_dict=None,
    global_dict=None,
    resolvers=(),
    level: int = 0,
    target=None,
    inplace: bool = False,
) -> Any:
    """
    Evaluate a Python expression as a string using various backends.

    .. warning::

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Use DataFrame.eval or DataFrame.query instead of pd.eval, so the calling frame supplies the locals scope.
  2. Drop the '@' and pass the variable through local_dict, e.g. pd.eval('x + 1', local_dict={'x': x}).
  3. If you must call pd.eval from a wrapper, forward level appropriately so stack walking reaches your caller's frame.

Example fix

// before
result = pd.eval('@x + 1')
// after
result = pd.eval('x + 1', local_dict={'x': x})
Defensive patterns

Strategy: validation

Validate before calling

def check_top_level_at(expr: str, level: int) -> None:
    import tokenize
    from pandas.core.computation.parsing import tokenize_string
    if level == 0:
        for toknum, tokval in tokenize_string(expr):
            if toknum == tokenize.OP and tokval == '@':
                raise ValueError(
                    "'@' is disallowed in top-level pd.eval; "
                    "use df.query/df.eval or pass local_dict"
                )

check_top_level_at(expr, level=0)

Type guard

def is_safe_for_top_eval(expr: str) -> bool:
    return '@' not in expr

Try / catch

try:
    pd.eval(expr)
except SyntaxError as e:
    if '@' in expr:
        # resolve locals explicitly and retry
        pd.eval(expr.replace('@', ''), local_dict=locals())
    else:
        raise

Prevention

When it happens

Trigger: pd.eval('@x + 1') called directly at module level, in a script, or in any frame where level resolves to 0 (the default level kwarg). Any direct pd.eval invocation that embeds a local-variable reference with '@'.

Common situations: Promoting a df.query('@threshold > col') snippet to a standalone pd.eval call without a DataFrame context. Notebooks where users expect @ to work like IPython magic. Calling pd.eval inside a helper but forgetting to forward level.

Related errors


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