views:

1210

answers:

3

Hello, I'm using boost::python to embed some python code into an app. I was able to get print statements or other expressions to be evaluated properly, but when I try to import modules, it is not importing and application is exiting. Further the globals() function call in the embedded code gives a runtime error too.

#include <boost/python.hpp>

using namespace boost;
using namespace boost::python;
using namespace boost::python::api;

int main(void) {
    Py_Initialize();
    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");
    main_namespace["urllib2"] = import("urllib2");

    object ignored = exec(
            "print 'time'\n", main_namespace);
}

Here, I've tried to import urllib2 using the boost import function, this compiles and runs properly, but with the following exec statement, it gives an error.

    object ignored = exec(
            "print urllib2\n"
            "print 'time'\n", main_namespace);

Or when I remove the boost import function and do the import from within the embedded code also, it gives an error. I tried using a try: except: block but that doesn't work either. Is this because the C++ app isn't able to find the location of the urllib2 py module or something? Is there a way to set the path of the module before trying to import?

This is being built only for internal use, so some hard coding of the paths is acceptable.

Edit: More info:
This is what happens. I did a try .. catch and called the PyErr_Print() when ever there is an exception, and got this as error all the time when there are module imports or even function calls. Error message:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: 'NoneType' object does not support item assignment

Can anyone think of any reason?

+2  A: 

If you haven't already, you need to

import sys
sys.path.append("/home/user/whatever")

That took care of my problems a couple of years ago when embedding boost::python (Python v2.5).

Edit:

Poked around in old code. Perhaps this does the trick:

Py_SetProgramName(argv[0]);
Py_InitializeEx(0);

Sounds unsure that you should really need the Py_SetProgramName(), but I faintly remember some fishy business there.

Jonas Byström
Thanks for the response, and really for the delay in trying it and telling you, Jonas.I tried it but this wasn't helping. I'm not even able to to get the program to run if it has this single statement "print globals()\n". I tried the same code on windows and linux, and same response everywhere.I'm using Boost 1.39.0
Sahasranaman MS
Oops. I hadn't noticed the edit. I'll try that too.
Sahasranaman MS
A: 

That didn't help, but I found a different solution to my problem. My current code looks like this:

#include <boost/python.hpp>
#include <iostream>

using namespace std;
using namespace boost;
using namespace boost::python;
using namespace boost::python::api;

int main(void) {
        Py_Initialize();
        boost::python::object http = boost::python::import("urllib2");

        try
        {
                boost::python::object response = http.attr("urlopen")("http://www.google.com");
                boost::python::object read = response.attr("read")();
                std::string strResponse = boost::python::extract<string>(read);
                cout << strResponse << endl;
        }
        catch(...)
        {
                PyErr_Print();
                PyErr_Clear();
        }
}

Anyways, thanks for the answer Jonas

Sahasranaman MS
Glad you got your code working. What do you want to do with it?
Jonas Byström
Oh. I was totally sick of the C++ sockets libraries. They need time to understand, and since I'm not a computers student, I take longer to understand. So I created something that feels like an asynchronous urllib++ with the python urllib2 in the back. :D I know it sounds crazy. But I have an extremely convenient solution now!
Sahasranaman MS
Hehe, good for you!
Jonas Byström
A: 

I ran into the same problem as you, i e a very simple example resulting in the TypeError, and found the answer in this question, which was to supply the namespace twice, both as global and local.

Magnus Österlind