Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does import work with Boost.Python from inside python files

I am using Boost.Python to embed an interpreter in my C++ executable and execute some prewritten scripts. I have got it working so that I can call functions in the python file but the python code I want to use imports external files and these imports fail because 'no module named '. If I run the script directly from python everything works as expected however.

So my question is what is the correct way of importing modules in python scripts that are being run via C++ bindings?

C++ Code:

#include "boost/python.hpp"

int main(int argc, char** argv)
{
  try
  {
    Py_Initialize();
    boost::python::object test = boost::python::import("__main__");
    boost::python::object testDict = test.attr("__dict__");
    boost::python::exec_file("test.py", testDict, testDict);

  }
  catch(boost::python::error_already_set& e)
  {
    PyErr_Print();
  }
return 0;

}

Python Code:

import ModuleX
like image 516
radman Avatar asked Feb 14 '12 22:02

radman


People also ask

How does boost Python work?

It allows you to quickly and seamlessly expose C++ classes functions and objects to Python, and vice-versa, using no special tools -- just your C++ compiler. It is designed to wrap C++ interfaces non-intrusively, so that you should not have to change the C++ code at all in order to wrap it, making Boost.

How does Python package import work?

When a package is imported, Python runs all of the code in the package's __init__.py file, if such a file exists. All of the objects defined in the module or the package's __init__.py file are made available to the importer.

What happens when a Python file is imported?

When a module is first imported, Python searches for the module and if found, it creates a module object 1, initializing it. If the named module cannot be found, a ModuleNotFoundError is raised. Python implements various strategies to search for the named module when the import machinery is invoked.


1 Answers

So it turns out that my problem is a simple case of the module search path not being set correctly when initialised from within C++.

From the Python Documentation intro:

On most systems (in particular, on Unix and Windows, although the details are slightly different), Py_Initialize() calculates the module search path based upon its best guess for the location of the standard Python interpreter executable, assuming that the Python library is found in a fixed location relative to the Python interpreter executable. In particular, it looks for a directory named lib/pythonX.Y relative to the parent directory where the executable named python is found on the shell command search path (the environment variable PATH).

So what this means is that the module search path is in no way set to point at the current working directory, rather it points at the system python install folder.

The solution for me was to correctly set the module search path to point at the current working directory. To do this you need to initialise python and then extract the sys.path value and add any additional paths. Excuse the use of boost if you're not into that; you should be able to easily see how to substitute any string desired.

Py_Initialize();

// now time to insert the current working directory into the python path so module search can take advantage
// this must happen after python has been initialised
boost::filesystem::path workingDir = boost::filesystem::absolute("./").normalize();
PyObject* sysPath = PySys_GetObject("path");
PyList_Insert( sysPath, 0, PyString_FromString(workingDir.string().c_str()));
like image 52
radman Avatar answered Nov 15 '22 16:11

radman