Search code examples
pythonc++abstract-syntax-treepython-c-apipybind11

Compile and execute AST using pybind11 or Python C API


I would like to translate this Python code into C++ code using Pybind11 or directly the Python C API:

import ast

code = "print('Hello World!')"

code_ast = ast.parse(code, mode="exec") # "code" being a string containing code
# ... perform some modifications on "code_ast"
exec(compile(code_ast, filename="<ast>", mode="exec"))

Here is what I have currently using Pybind11:

#include <iostream>
#include "pybind11/embed.h"

namespace py = pybind11;

std::string code = "print('Hello World!')";

py::module ast = py::module::import("ast");
py::module builtins = py::module::import("builtins");

py::object code_ast = ast.attr("parse")(code, "<unknown>", "exec");
// ... perform some modifications on "code_ast"
py::object compiled_code = builtins.attr("compile")(code_ast, "<ast>", "exec");
builtins.attr("exec")(compiled_code);

Unfortunately, the last line of C++ code raises a runtime error: SystemError: frame does not exist.

I'm not sure I understand this error, I tried to pass the globals and locals to exec, it didn't solve the issue.

EDIT: When passing globals, it says: SystemError: <built-in function globals> returned NULL without setting an error

Any idea on how to achieve this properly?


Solution

  • I found my answer, I wasn't using the globals function from Pybind11, but the built-in one from Python (builtins.attr("globals")()).

    This is the working version:

    py::module ast = py::module::import("ast");
    py::module builtins = py::module::import("builtins");
    
    py::object code_ast = ast.attr("parse")(code, "<unknown>", "exec");
    // ... perform some modifications on "code_ast"
    py::object compiled_code = builtins.attr("compile")(code_ast, "<ast>", "exec");
    builtins.attr("exec")(compiled_code, py::globals());