Problem with embedding python with boost.python
I try to embed python in a C++ program. I wrote a module that works if I compile it for extending python ... and I wrote a simple interpreter that works as an embedded one ! But now I want to extend the embedded interpreter to allow access to the C++ classes ! The extension module is called "editor". My problem is that whenever I try to import the module in python, the program just abort ... the import is done by evaluating 'import edtitor' inside the python interpreter ... I show you the interpreter code ! Of course, the module is defined using boost.python ! An extra-question is : if I use Py_file_input, I can't retrieve the output of the python command, if I use Py_single_input, I can retrieve the output, but I can't do any assignment or printing ... how can I have both ??? I really begin to think python was not designed to embedded :( Tthe interpreter class is : class PythonWnd : public QMainWindow { Q_OBJECT public: PythonWnd(QWidget * parent = 0, const char * name = 0, WFlags f = WType_TopLevel); ~PythonWnd(); public slots: void evalCommand(); protected: void LaunchPython(); void ClosePython(); void ExecutePython(const QString command); QTextEdit *output; QLineEdit *edit; QVBox *layout; boost::python::object main_module; boost::python::dict main_namespace; }; The initialization function that is : void PythonWnd::LaunchPython() { // register the module editor with the interpreter if (PyImport_AppendInittab("editor", init_module_editor) == -1) { cerr << "Error, cannot load editor module !" << endl; throw std::runtime_error("Failed to add embedded_hello to the interpreter's " "builtin modules"); } // Initialize the interpreter Py_Initialize(); // Get the main module ... main_module = object(boost::python::handle<>(borrowed(PyImport_AddModule("__main__")))); main_namespace = dict(boost::python::handle<>(borrowed(PyModule_GetDict(main_module.ptr())))); } Then the interpretor function (launched each time a line is to be evaluated ... it's an iteractive interpretor) is: void PythonWnd::ExecutePython(const QString command) { char *cmd; cmd = new char[command.length()+1]; strcpy(cmd, command.latin1()); stringstream ss; try { PyObject *obj = PyRun_String(cmd, Py_file_input, main_namespace.ptr(), main_namespace.ptr()); boost::python::handle<> hres(obj); boost::python::object res(hres); //std::string res_cmd = extract<std::string>(res.attr("__repr__")()); std::string res_cmd = call_method<std::string>(res.ptr(),"__repr__"); ss << res_cmd; } catch(error_already_set) { PyErr_Print(); ss << "Error, exception thrown ... look at console for details" << endl; PyObject *exception, *v, *tb, *hook; } output->append(ss.str().c_str()); delete cmd; } -- Pierre Barbier de Reuille INRA - UMR Cirad/Inra/Cnrs/Univ.MontpellierII AMAP Botanique et Bio-informatique de l'Architecture des Plantes TA40/PSII, Boulevard de la Lironde 34398 MONTPELLIER CEDEX 5, France tel : (33) 4 67 61 65 77 fax : (33) 4 67 61 56 68
Pierre Barbier de Reuille wrote:
I try to embed python in a C++ program. I wrote a module that works if I compile it for extending python ... and I wrote a simple interpreter that works as an embedded one !
But now I want to extend the embedded interpreter to allow access to the C++ classes !
The extension module is called "editor". My problem is that whenever I try to import the module in python, the program just abort ... the import is done by evaluating 'import edtitor' inside the python interpreter ... I show you the interpreter code ! Of course, the module is defined using boost.python !
An extra-question is : if I use Py_file_input, I can't retrieve the output of the python command, if I use Py_single_input, I can retrieve the output, but I can't do any assignment or printing ... how can I have both ??? I really begin to think python was not designed to embedded :(
Well this is not really a question of embedding or not. Python's eval, exec, and execfile statements work in the same way. Anyway, what you could do is use Py_file_input for all but the last line of code, then use Py_eval_input for the last line. Or you could let the code store the result in a variable and read that variable from the main_namespace: PyObject *obj = PyRun_String("result = 5**2", Py_file_input, main_namespace.ptr(), main_namespace.ptr()); boost::python::handle<> hres(obj); boost::python::object res = main_namespace["result"];
Tthe interpreter class is :
class PythonWnd : public QMainWindow { Q_OBJECT
public: PythonWnd(QWidget * parent = 0, const char * name = 0, WFlags f = WType_TopLevel); ~PythonWnd();
public slots: void evalCommand();
protected: void LaunchPython(); void ClosePython(); void ExecutePython(const QString command); QTextEdit *output; QLineEdit *edit; QVBox *layout; boost::python::object main_module; boost::python::dict main_namespace;
};
The initialization function that is :
void PythonWnd::LaunchPython() { // register the module editor with the interpreter if (PyImport_AppendInittab("editor", init_module_editor) == -1) { cerr << "Error, cannot load editor module !" << endl; throw std::runtime_error("Failed to add embedded_hello to the interpreter's " "builtin modules"); } // Initialize the interpreter Py_Initialize(); // Get the main module ... main_module = object(boost::python::handle<>(borrowed(PyImport_AddModule("__main__")))); main_namespace = dict(boost::python::handle<>(borrowed(PyModule_GetDict(main_module.ptr()))));
}
Then the interpretor function (launched each time a line is to be evaluated ... it's an iteractive interpretor) is:
void PythonWnd::ExecutePython(const QString command) { char *cmd; cmd = new char[command.length()+1]; strcpy(cmd, command.latin1()); stringstream ss; try { PyObject *obj = PyRun_String(cmd, Py_file_input, main_namespace.ptr(), main_namespace.ptr()); boost::python::handle<> hres(obj); boost::python::object res(hres); //std::string res_cmd = extract<std::string>(res.attr("__repr__")()); std::string res_cmd = call_method<std::string>(res.ptr(),"__repr__"); ss << res_cmd; } catch(error_already_set) { PyErr_Print(); ss << "Error, exception thrown ... look at console for details" << endl; PyObject *exception, *v, *tb, *hook; } output->append(ss.str().c_str()); delete cmd; }
command.latin1() is not null-terminated I think, which would make the strcpy fail. (Not sure, I don't use Qt.) Wouldn't it be easier and safer to do: QString cmd = command + '\0'; ... PyObject *obj = PyRun_String(cmd.latin1(), Py_file_input, main_namespace.ptr(), main_namespace.ptr()); ? Are you sure the interpreter aborts only when you import your module? If latin1() does what I think it does then your program could crash on any piece of Python code. Regards, Dirk Gerrits
Dirk Gerrits wrote:
command.latin1() is not null-terminated I think, which would make the strcpy fail. (Not sure, I don't use Qt.) Wouldn't it be easier and safer to do:
QString cmd = command + '\0'; ... PyObject *obj = PyRun_String(cmd.latin1(), Py_file_input, main_namespace.ptr(), main_namespace.ptr());
?
Ah I just read that latin1() DOES provide a null-terminated string. Sorry. But then I don't understand why you were copying it in the first place. Care to elaborate? Regards, Dirk Gerrits
I copied it just because the PyRun_String command take a "char *" and that latin1 returns a "const char*" ... si you have to copy it ! For my other problem, I just solved it ... the solution is to replace the AppendInittab by : PyImport_AppendInittab("editor", initeditor) and ti define initeditor with : extern "C" void initeditor(); ... when testing I forgot the 'extern "C"' ! Now it works and I'm working on your recommandations to retrieve the output ! Tanks ! Dirk Gerrits wrote:
Dirk Gerrits wrote:
command.latin1() is not null-terminated I think, which would make the strcpy fail. (Not sure, I don't use Qt.) Wouldn't it be easier and safer to do:
QString cmd = command + '\0'; ... PyObject *obj = PyRun_String(cmd.latin1(), Py_file_input, main_namespace.ptr(), main_namespace.ptr());
?
Ah I just read that latin1() DOES provide a null-terminated string. Sorry.
But then I don't understand why you were copying it in the first place. Care to elaborate?
Regards, Dirk Gerrits
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
-- Pierre Barbier de Reuille INRA - UMR Cirad/Inra/Cnrs/Univ.MontpellierII AMAP Botanique et Bio-informatique de l'Architecture des Plantes TA40/PSII, Boulevard de la Lironde 34398 MONTPELLIER CEDEX 5, France tel : (33) 4 67 61 65 77 fax : (33) 4 67 61 56 68
Pierre Barbier de Reuille wrote:
I copied it just because the PyRun_String command take a "char *" and that latin1 returns a "const char*" ... si you have to copy it !
Well PyRun_String doesn't actually modify your string. In fact, in the Python CVS it takes a const char* as an argument. For now, you can just use const_cast<char*>(command.latin1()) as an argument, no copying necessary.
For my other problem, I just solved it ... the solution is to replace the AppendInittab by : PyImport_AppendInittab("editor", initeditor)
and ti define initeditor with :
extern "C" void initeditor();
... when testing I forgot the 'extern "C"' !
Now it works and I'm working on your recommandations to retrieve the output !
Tanks !
You're welcome. By the way, there are plans to make embedding with Boost.Python significantly easier. I'm not sure when they will be fully implemented, but hopefully before the end of this summer. Any new developments will be posted here on the C++ SIG.
Dirk Gerrits wrote:
Dirk Gerrits wrote:
command.latin1() is not null-terminated I think, which would make the strcpy fail. (Not sure, I don't use Qt.) Wouldn't it be easier and safer to do:
QString cmd = command + '\0'; ... PyObject *obj = PyRun_String(cmd.latin1(), Py_file_input, main_namespace.ptr(), main_namespace.ptr());
?
Ah I just read that latin1() DOES provide a null-terminated string. Sorry.
But then I don't understand why you were copying it in the first place. Care to elaborate?
Regards, Dirk Gerrits
participants (2)
-
Dirk Gerrits -
Pierre Barbier de Reuille