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

Question:

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
Asked By: radman

||

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()));
Answered By: radman

I know this is very old and there’s already a solid answer, but I get the same effect with a simple

exec("import sys nsys.path.append('.')");

Correct me if I’m wrong.

Answered By: Kingsley Oyelabi