简体   繁体   中英

libboost-python create module in C++-runtime and use it from pythin code, for python 3.x

I have a piece of software that embeds python using libboost-python. It creates a python module in C++ runtime, and then imports it from python code.

For python2.7 this works like this:

test_module.py

import _hello_provider

test_var = 42

def hello_static():
        return "Hello world static"

def hello_provided():
        return _hello_provider.provide_hello()

test27.cpp

#include <boost/python.hpp>
 
std::string provide_hello() {
    return "hello world provided";
}
 
BOOST_PYTHON_MODULE(_hello_provider) {
    using namespace boost::python;
    def("provide_hello", &provide_hello);
}


int main()
{
  Py_InitializeEx(0);
  try {
    boost::python::object modImp = boost::python::import("imp");
    init_hello_provider();


    PyImport_AddModule("test_module");

    modImp.attr("load_source")("test_module", "test_module.py");

    boost::python::exec("print(test_var)", boost::python::import("test_module").attr("__dict__"));
    boost::python::exec("print(hello_static())", boost::python::import("test_module").attr("__dict__"));
    boost::python::exec("print(hello_provided())", boost::python::import("test_module").attr("__dict__"));
  }
  catch (const boost::python::error_already_set&)
  {
    PyErr_Print();
  }
  Py_Finalize();
}

On Debian Stretch you can build it like this:

g++  test27.cpp  -I /usr/include/x86_64-linux-gnu/python2.7 -I /usr/include/python2.7 -lboost_python -lboost_system -lpython2.7

It works as expected. Python code imports _hello_provider module that have been created in C++ code, and uses provide_hello() for printing.

When I am trying to port this to python 3.x I am getting code like that:

test3x.cpp

#include <boost/python.hpp>
 
std::string provide_hello() {
    return "hello world provided";
}

BOOST_PYTHON_MODULE(_hello_provider) {
    using namespace boost::python;
    def("provide_hello", &provide_hello);
}


int main()
{
  Py_InitializeEx(0);
  try {
    boost::python::object modImp = boost::python::import("imp");
    PyInit__hello_provider();

    PyImport_AddModule("test_module");

    modImp.attr("load_source")("test_module", "test_module.py");

    boost::python::exec("print(test_var)", boost::python::import("test_module").attr("__dict__"));
    boost::python::exec("print(hello_static())", boost::python::import("test_module").attr("__dict__"));
    boost::python::exec("print(hello_provided())", boost::python::import("test_module").attr("__dict__"));
  }
  catch (const boost::python::error_already_set&)
  {
    PyErr_Print();
  }
  Py_Finalize();
}

It uses same test_module.py as previous example. On Debian Bullseye you can successfully build it using following command:

g++ test3x.cpp  -I /usr/include/x86_64-linux-gnu/python3.9 -I /usr/include/python3.9 -lpython3.9  -lboost_python39 -lboost_system

It will be successfully built, but will not work as expected, saying

Traceback (most recent call last):
  File "/usr/lib/python3.9/imp.py", line 169, in load_source
    module = _exec(spec, sys.modules[name])
  File "<frozen importlib._bootstrap>", line 613, in _exec
  File "<frozen importlib._bootstrap_external>", line 790, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "test_module.py", line 1, in <module>
    import _hello_provider
ModuleNotFoundError: No module named '_hello_provider'

If I remove "import" part from the code, everything works well.

How I should properly create python module in C++ code and import it from python code in libboost-python3.x? What is the right way?

Key point here was, that in python3.x you should manually add such modules to inittab(whatever it is) using PyImport_AppendInittab

Finally my sample that builds with both 2.7 anx 3.x pythons looks like this

#include <boost/python.hpp>

std::string provide_hello() {
    return "hello world provided";
}

BOOST_PYTHON_MODULE(_hello_provider) {
    using namespace boost::python;
    def("provide_hello", &provide_hello);
}

int main()
{
#if PY_MAJOR_VERSION >= 3
  PyImport_AppendInittab((char*)"_hello_provider", PyInit__hello_provider);
#else
  PyImport_AppendInittab((char*)"_hello_provider", init_hello_provider);
#endif
  Py_InitializeEx(0);

  try {
    boost::python::object modImp = boost::python::import("imp");
    PyImport_AddModule("test_module");

    modImp.attr("load_source")("test_module", "test_module.py");

    boost::python::exec("print(test_var)", boost::python::import("test_module").attr("__dict__"));
    boost::python::exec("print(hello_static())", boost::python::import("test_module").attr("__dict__"));
    boost::python::exec("print(hello_provided())", boost::python::import("test_module").attr("__dict__"));
  }
  catch (const boost::python::error_already_set&)
  {
    PyErr_Print();
  }
  Py_Finalize();
}

Special thanks to https://github.com/TNG/boost-python-examples/tree/main/10-Embedding authors and O02eg from linux.org.ru

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM