pandas-dev/pandas · error · SyntaxError

The '@' prefix is only supported by the pandas parser

Error message

The '@' prefix is only supported by the pandas parser

What it means

The '@' prefix is a pandas-specific extension that lets an expression string reference a Python local variable; it works by token-rewriting '@x' into an internal sentinel via _replace_locals (expr.py:99) during the pandas preparser pass. When you pass parser='python', that preparser is bypassed (PythonExprVisitor uses identity preparser, expr.py:800), so the raw '@' token is meaningless and _check_for_locals rejects it before parsing. The library throws because the python parser has no mechanism to bind '@' to a stack-frame variable.

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. Drop parser='python' and use the default parser='pandas' so the '@' prefix is honored.
  2. Remove the '@' prefix and inject the variable explicitly via local_dict={'b': b}.
  3. If you need python-parser semantics, pre-resolve the variable into the string with an f-string (only for trusted, non-user input).

Example fix

// before
pd.eval('a + @b', parser='python')
// after
pd.eval('a + b', parser='python', local_dict={'b': b})
Defensive patterns

Strategy: validation

Validate before calling

def check_at_prefix(expr: str, parser: str) -> None:
    import tokenize
    from pandas.core.computation.parsing import tokenize_string
    if parser != 'pandas':
        for toknum, tokval in tokenize_string(expr):
            if toknum == tokenize.OP and tokval == '@':
                raise ValueError(
                    "'@' prefix requires parser='pandas'; "
                    "remove '@' or switch parser"
                )

# call before pd.eval:
check_at_prefix(expr, parser)

Type guard

def is_at_free_for_python_parser(expr: str, parser: str) -> bool:
    return parser == 'pandas' or '@' not in expr

Try / catch

try:
    pd.eval(expr, parser=parser)
except SyntaxError as e:
    if '@' in expr and parser != 'pandas':
        # retry with pandas parser or strip locals
        pd.eval(expr, parser='pandas')
    else:
        raise

Prevention

When it happens

Trigger: Calling pd.eval('a + @b', parser='python') or df.query('col > @threshold', parser='python'). Anything that combines the literal '@' character in the expression string with parser set to a value other than the default 'pandas'.

Common situations: Switching parser to 'python' to gain strict Python semantics (e.g. floor division) while keeping existing '@'-prefixed variable references copied from a working df.query call. Copy-pasting query expressions between code paths that use different parsers. User-supplied filter strings that happen to contain '@'.

Related errors


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