Search code examples
boost-pythonpython-embedding

Boost python, calling function objects with a namespace


I am embedding python in my C++ application, using boost python.

I would like to be able to call a boost python function object, and associate a global name space with that function call. Specifically, the simplified relevant code is:

bp::object main = bp::import("__main__");
bp::object main_namespace = main.attr("__dict__");


//Put the function name runPyProg in the main_namespace

bp::object PyProg = exec(
        "import cStringIO\n"
        "import sys\n"
        "sys.stderr = cStringIO.StringIO()\n"
        "def runPyProg(exp):\n"
        "    print exp\n"
        "    exec(exp)\n"
        "    return\n"
        "\n",main_namespace);

//Now call the python function runPyProg with an argument

bp::object py_fn = main.attr("runPyProg");
py_fn(expStr)

I know that when I use the boost python exec() function, I can send in the global namespace, as shown above. My question is how do I associate main_namespace with the python function when I call py_fn? My final goal is that local variables from runPyProg will be placed in the main_namespace.

Thank you.


Solution

  • If I understand the question correctly, then it should be as simple as specifying the context in which exec will execute. A function or method can access the namespace in which it is defined via globals(). Thus, calling globals() from within runPyProg() will return the Python equivalent of main_namespace. Additionally, exec takes two optional arguments:

    • The first argument specifies the dictionary that will be used for globals(). If the second argument is omitted, then it is also used for locals().
    • The second argument specifies the dictionary that will be used for locals(). Variable changes occurring within exec are applied to locals().

    Therefore, change:

    exec exp
    

    to

    exec exp in globals()
    

    and it should provide the desired behavior, where exp can interact with global variables in main_namespace.


    Here is a basic example:

    #include <boost/python.hpp>
    
    int main()
    {
      Py_Initialize();
    
      namespace python = boost::python;
      python::object main = python::import("__main__");
      python::object main_namespace = main.attr("__dict__");
    
      //Put the function name runPyProg in the main_namespace
      python::exec(
        "def runPyProg(exp):\n"
        "    print exp\n"
        "    exec exp in globals()\n"
        "    return\n"
        "\n", main_namespace);
    
      // Now call the python function runPyProg with an argument
      python::object runPyProg = main.attr("runPyProg");
    
      // Set x in python and access from C++.
      runPyProg("x = 42");
      std::cout << python::extract<int>(main.attr("x")) << std::endl;
    
      // Set y from C++ and access within python.
      main.attr("y") = 100;
      runPyProg("print y");
    
      // Access and modify x in python, then access from C++.
      runPyProg("x += y");
      std::cout << python::extract<int>(main.attr("x")) << std::endl;
    }
    

    Commented output:

    x = 42          // set from python
    42              // print from C++
                    // y set to 100 from C++
    print y         // print y from python
    100             //
    x += y          // access and modify from python
    142             // print x from C++