{"record":{"id":"adad58cb58425bec","repo":"pandas-dev/pandas","slug":"node-name-nodes-are-not-implemented","errorCode":null,"errorMessage":"'{node_name}' nodes are not implemented","messagePattern":"'(.+?)' nodes are not implemented","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"pandas/core/computation/expr.py","lineNumber":267,"sourceCode":"    | _unsupported_expr_nodes\n) - _hacked_nodes\n\n# we're adding a different assignment in some cases to be equality comparison\n# and we don't want `stmt` and friends in their so get only the class whose\n# names are capitalized\n_base_supported_nodes = (_all_node_names - _unsupported_nodes) | _hacked_nodes\nintersection = _unsupported_nodes & _base_supported_nodes\n_msg = f\"cannot both support and not support {intersection}\"\nassert not intersection, _msg\n\n\ndef _node_not_implemented(node_name: str) -> Callable[..., None]:\n    \"\"\"\n    Return a function that raises a NotImplementedError with a passed node name.\n    \"\"\"\n\n    def f(self, *args, **kwargs):\n        raise NotImplementedError(f\"'{node_name}' nodes are not implemented\")\n\n    return f\n\n\n# should be bound by BaseExprVisitor but that creates a circular dependency:\n# _T is used in disallow, but disallow is used to define BaseExprVisitor\n# https://github.com/microsoft/pyright/issues/2315\n_T = TypeVar(\"_T\")\n\n\ndef disallow(nodes: set[str]) -> Callable[[type[_T]], type[_T]]:\n    \"\"\"\n    Decorator to disallow certain nodes from parsing. Raises a\n    NotImplementedError instead.\n\n    Returns\n    -------\n    callable","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/computation/expr.py#L249-L285","documentation":"The expression visitor is built with the @disallow decorator (expr.py:346/779) which installs visit_<Node> methods that raise NotImplementedError for an explicit unsupported set (Lambda, Yield, IfExp, DictComp, SetComp, GeneratorExp, Repr, Set, Is, IsNot, plus statement/module/handler nodes). When the AST produced by Python's parse contains any such node, dispatch lands on _node_not_implemented and raises naming the node type. This bounds the eval grammar to what numexpr/pandas can actually compute.","triggerScenarios":"df.eval('a if b else c') (IfExp), df.query('a is None') (Is/IsNot), df.eval('lambda x: x') (Lambda), df.eval('(x for x in a)') (GeneratorExp), df.eval('{k: v for ...}') (DictComp).","commonSituations":"Porting arbitrary Python one-liners into eval strings. Using 'is None' checks in query. Expecting ternary or comprehension support. Upgrading Python versions that emit different AST node names.","solutions":["Rewrite the logic using supported operators (e.g. replace ternary with np.where outside eval).","Move the unsupported construct into plain Python and feed its result back as a column.","For None checks, fillna or use a boolean mask instead of 'is' inside query."],"exampleFix":"// before\ndf.eval('result = a if flag else b')\n// after\ndf['result'] = np.where(df['flag'], df['a'], df['b'])","handlingStrategy":"validation","validationCode":"import ast\n\nUNSUPPORTED = {\n    'Lambda', 'Yield', 'GeneratorExp', 'IfExp', 'DictComp',\n    'SetComp', 'Repr', 'Set', 'Is', 'IsNot',\n}\n\ndef validate_eval_ast(expr: str) -> None:\n    tree = ast.parse(expr, mode='eval')\n    present = {type(n).__name__ for n in ast.walk(tree)}\n    bad = present & UNSUPPORTED\n    if bad:\n        raise NotImplementedError(\n            f'eval does not support these nodes: {sorted(bad)}'\n        )\n\nvalidate_eval_ast(expr)","typeGuard":"import ast\n\ndef uses_only_supported_nodes(expr: str) -> bool:\n    UNSUPPORTED = {\n        'Lambda', 'Yield', 'GeneratorExp', 'IfExp',\n        'DictComp', 'SetComp', 'Repr', 'Set', 'Is', 'IsNot',\n    }\n    present = {type(n).__name__ for n in ast.walk(ast.parse(expr, mode='eval'))}\n    return not (present & UNSUPPORTED)","tryCatchPattern":"try:\n    df.eval(expr)\nexcept NotImplementedError as e:\n    if 'nodes are not implemented' in str(e):\n        # fall back to plain Python computation\n        df['result'] = eval(compile(ast.parse(expr, mode='eval'), '<eval>', 'eval'), {}, df.to_dict('series'))\n    else:\n        raise","preventionTips":["Avoid ternaries, lambdas, comprehensions, and 'is'/'is not' inside eval strings.","Pre-compute unsupported constructs in Python and feed results as columns.","When accepting user expressions, validate the AST against the supported set first."],"tags":["pandas","eval","ast","unsupported","grammar"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}