pandas-dev/pandas · error · ValueError

Invalid Attribute context {type(ctx).__name__}

Error message

Invalid Attribute context {type(ctx).__name__}

What it means

visit_Attribute (expr.py:643) only handles ast.Load context — i.e. reading an attribute like df.col. Any other context (ast.Store for assignment targets, ast.Del for deletion) is meaningless inside an eval expression and falls through to raise ValueError naming the context class. In practice the visit_Assign check for ast.Name usually catches attribute-LHS first, so this fires for unusual attribute-in-store-context AST shapes.

Source

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

        ctx = node.ctx
        if isinstance(ctx, ast.Load):
            # resolve the value
            visited_value = self.visit(value)
            if hasattr(visited_value, "value"):
                resolved = visited_value.value
            else:
                resolved = visited_value(self.env)
            try:
                v = getattr(resolved, attr)
                name = self.env.add_tmp(v)
                return self.term_type(name, self.env)
            except AttributeError:
                # something like datetime.datetime where scope is overridden
                if isinstance(value, ast.Name) and value.id == attr:
                    return resolved
                raise

        raise ValueError(f"Invalid Attribute context {type(ctx).__name__}")

    def visit_Call(self, node, side=None, **kwargs):
        if isinstance(node.func, ast.Attribute) and node.func.attr != "__call__":
            res = self.visit_Attribute(node.func)
        elif not isinstance(node.func, ast.Name):
            raise TypeError("Only named functions are supported")
        else:
            try:
                res = self.visit(node.func)
            except UndefinedVariableError:
                # Check if this is a supported function name
                try:
                    res = FuncNode(node.func.id)
                except ValueError:
                    # Raise original error
                    raise

        if res is None:

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Rewrite so attributes are only read, never assigned/deleted inside eval.
  2. Do attribute assignment in plain Python outside the eval string.
  3. If building AST programmatically, ensure Attribute nodes use ast.Load.

Example fix

// before
# attribute in a store context (rare, usually hand-built AST)
// after
# assign to a plain name and set the attribute in Python:
df.eval('tmp = a + b')
df.obj.tmp = df['tmp']
Defensive patterns

Strategy: validation

Validate before calling

import ast

def validate_attribute_load_only(expr: str) -> None:
    for node in ast.walk(ast.parse(expr, mode='eval')):
        if isinstance(node, ast.Attribute) and not isinstance(node.ctx, ast.Load):
            raise ValueError(
                f'attribute in {type(node.ctx).__name__} context not supported'
            )

validate_attribute_load_only(expr)

Type guard

import ast

def attributes_are_load_only(expr: str) -> bool:
    return all(
        isinstance(n.ctx, ast.Load)
        for n in ast.walk(ast.parse(expr, mode='eval'))
        if isinstance(n, ast.Attribute)
    )

Try / catch

try:
    df.eval(expr)
except ValueError as e:
    if 'Invalid Attribute context' in str(e):
        # move attribute assignment out of eval into Python
        setattr(obj, attr, value)
    raise

Prevention

When it happens

Trigger: An attribute access appearing in a Store or Del AST context inside the expression tree — typically from hand-built AST or from preprocessing that produces non-Load attribute nodes.

Common situations: Internal tooling that constructs AST nodes directly. Parsers/preparsers that change node contexts. Very rarely reachable from user strings because earlier checks reject attribute assignment.

Related errors


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