How does import work with Boost.Python from inside

2019-04-07 12:29发布

问题:

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

回答1:

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()));