boost + python + redirect PyErr_Print

Started by
8 comments, last by NoRulez 15 years, 12 months ago
Hey @all, can anybody explain me how to redirect the "PyErr_Print()" to boost::python or to a std::string or equivalent? My Problem is that I have a GUI Application, so i don't have a console window where a python error can be printed with "PyErr_Print()". I need to redirect the output of "PyErr_Print()" and show this message in a MessageBox like the following:

try {
.
.
.
}catch(AnyPythonERROR &e) {
    // PyErr_Print();          // <------ This output must be shown in the MessageBox
    MessageBox("HERE COMES THE PYTHON ERROR (BACKTRACE)");
}

I 've already tried the following:

catch (bp::error_already_set const&) {
    bp::str blubb = bp::import("sys").attr("stderr").attr("data");
}

but with this i became the following error:

error: conversion from `boost::python::api::proxy<boost::python::api::attribute_policies>' to non-scalar type `boost::python::str' requested 	

I have the following installed .) Python 2.5.2 .) Boost 1_35_0 .) mingw32-make 3.80 Kind Regards NoRulez [Edited by - NoRulez on April 23, 2008 4:58:56 AM]
Advertisement
You could replace sys.stderr with your own wrapped C++ class. stdout and stderr can be replaced by any Python class with a method named "write" accepting a single argument. So you could create the class in C++ with a method that displays a message box, and wrap this class using boost::python. Then just assign a instance of this class to sys.stderr.
Thanks for the reply,

could you give me an example or some code snippet,
because i'm very new to boost.python and embedded python for c++ and so i 've no idea on how to write this code/workaround.

Kind Regards
NoRulez
Well, something like
class Foo {public:    void Bar( std::string const& message ) {        MessageBox( message );    }};BOOST_PYTHON_MODULE(whatever) {    class_< Foo >( "Foo", init_t<>() )        .def( "write", &Foo::Bar )        ;}

then somewhere after PyInitialize()
Foo foo;import("sys").attr("stderr") = foo;

This is totally untested, though! I'm writing this from memory :)
thanks,

i hope i didn't miss understood it :(
Here is my complete test application:
#include <boost/python.hpp>#include <boost/python/import.hpp>#include <boost/python/detail/api_placeholder.hpp>#include <QtCore/QCoreApplication>#include <QString>#include <iostream>#include <string>#include <QDebug>namespace bp = boost::python;using namespace std;class Foo {public:    std::string m_s;    void Bar( std::string const& message ) {        //MessageBox( message );        m_s = message;    }};BOOST_PYTHON_MODULE(whatever) {    bp::class_<Foo>("Foo")    //bp::class_< Foo >( "Foo", boost::init_t<>() )        .def( "write", &Foo::Bar )        ;}int main(int argc, char *argv[]) {    QCoreApplication app(argc, argv);    // Test python script    QString python_code("# -*- coding: iso-8859-15 -*-\n"                        "result )= 5 ** 2\n"                        "OUT = {'language' : 'German', 'name' : 'Max Mustermann', 'age' : 28}");    Py_Initialize();    Foo foo;    bp::import("sys").attr("stderr") = foo;    try {        PyRun_SimpleString(python_code.toStdString().c_str());//"result = 5 ** 2");        bp::object module(bp::handle<>(bp::borrowed(PyImport_AddModule("__main__"))));        bp::object dictionary = module.attr("__dict__");        bp::object result = dictionary["result"];        //double result_value = extract<double>(result);        {            bp::extract<int> x(result);            if (x.check()) {                int v = x();                qDebug() << "Extract Type: Integer = " << QString("%1").arg(v);            }        }{            bp::extract<double> x(result);            if (x.check()) {                double v = x();                qDebug() << "Extract Type: Double = " << QString("%1").arg(v);            }        }{            bp::extract<bool> x(result);            if (x.check()) {                bool v = x();                qDebug() << "Extract Type: Boolean = " << QString("%1").arg(v);            }        }{            bp::extract<long> x(result);            if (x.check()) {                long v = x();                qDebug() << "Extract Type: long = " << QString("%1").arg(v);            }        }{            bp::extract<std::string> x(result);            if (x.check()) {                std::string v = x();                qDebug() << "Extract Type: std::string = " << QString("%1").arg(v.c_str());            }        }    }    catch(...) {        cerr << "Out1: " << foo.m_s << endl;    }    cerr << "Out2: " << foo.m_s << endl;    Py_Finalize();    return 0;}

the only thing there is that i became no output, this output is only from qt, but where is my python error?
C:\examples\boost_python_example\release>boost_python_example.exeThis application has requested the Runtime to terminate it in an unusual way.Please contact the application's support team for more information.C:\examples\boost_python_example\release>


Is it so difficult or i'm confused?

Regards
NoRulez
This is what I'm doing :

I have this python file 'Erreur.py':
import sys, PG_Intern_ErrorRedirectStdout = 1class Redirect:    def write(self, s):        PG_Intern_Error.PrintError(s)def SetRedirect( redir ):	global RedirectStdout	RedirectStdout = redir	def Redirection():	global RedirectStdout	if ( RedirectStdout ):		sys.stderr = sys.stdout = Redirect()


Then when I init my embedded python, I added this C++ code :
initPG_Intern_Error();int	Ok = PyRun_SimpleString("import sys, PG_Intern_Error");		if ( Ok != -1 )		{			Ok = PyRun_SimpleString("import Erreur");			if ( Ok != -1 )			{				Ok = PyRun_SimpleString("Erreur.Redirection()");			}			else			{				PyErr_Print();			}		}



With PG_Intern_Error an own-made C module ( made with SWIG, not boost, but it is basically the same ) that define the printError function :

void	PrintError(PyObject* obj)	{ SendLog( "PythonOut", "%s",PyString_AsString(obj)); }


( I use an external log viewer ).


and that's all...


Hope it helps,

Emmanuel
Ok, a couple of things missing:
1) You must tell Python about your module before you can use it. This can be done in a number of ways, but I find PyImport_AppendInittab easiest to use.

2) You still have to call PyErr_Print() to print the error to stderr.

3) Not a error as such, but you want to call PyErr_Clear() after PyErr_Print, unless you wont be using Python again.

int main(int argc, char *argv[]) {    QCoreApplication app(argc, argv);    // Test python script    QString python_code("# -*- coding: iso-8859-15 -*-\n"                        "result )= 5 ** 2\n"                        "OUT = {'language' : 'German', 'name' : 'Max Mustermann', 'age' : 28}");    PyImport_AppendInittab( "module_name", initFoo ); // <-- Note that initFoo is auto generated    Py_Initialize();    Foo foo;    bp::import("sys").attr("stderr") = foo;    try {        PyRun_SimpleString(python_code.toStdString().c_str());//"result = 5 ** 2");        bp::object module(bp::handle<>(bp::borrowed(PyImport_AddModule("__main__"))));        bp::object dictionary = module.attr("__dict__");        bp::object result = dictionary["result"];        //double result_value = extract<double>(result);        {            bp::extract<int> x(result);            if (x.check()) {                int v = x();                qDebug() << "Extract Type: Integer = " << QString("%1").arg(v);            }        }{            bp::extract<double> x(result);            if (x.check()) {                double v = x();                qDebug() << "Extract Type: Double = " << QString("%1").arg(v);            }        }{            bp::extract<bool> x(result);            if (x.check()) {                bool v = x();                qDebug() << "Extract Type: Boolean = " << QString("%1").arg(v);            }        }{            bp::extract<long> x(result);            if (x.check()) {                long v = x();                qDebug() << "Extract Type: long = " << QString("%1").arg(v);            }        }{            bp::extract<std::string> x(result);            if (x.check()) {                std::string v = x();                qDebug() << "Extract Type: std::string = " << QString("%1").arg(v.c_str());            }        }    }    catch(...) {        PyErr_Print(); // <--         PyErr_Clear(); // <--         cerr << "Out1: " << foo.m_s << endl;    }    cerr << "Out2: " << foo.m_s << endl;    Py_Finalize();    return 0;}
Again, I can't test the code frem here, but I believe it should work.

When doing boost::python bindings, these three pages are your friends:
Python/C API Reference Manual
boost::python tutorial
boost::pytho n reference manual
@Promethium:
Thank for your help,

if you have time, can you save my life and give me an minimal compilable example, so you have said "Again, I can't test the code from here, but I believe it should work."
But i can't get it to work.

Kind Regards
NoRulez
Simple example displaying python errors in a message box:
#include <string>#include <boost/shared_ptr.hpp>#include <boost/python.hpp>#include <windows.h>class PyInitializer {public:  PyInitializer()  { Py_Initialize(); }  ~PyInitializer() { Py_Finalize(); }private:  PyInitializer(const PyInitializer &);  PyInitializer & operator=(const PyInitializer &);};int main(int argc, char* argv[]) {  PyInitializer py;  try {    PyRun_SimpleString("import cStringIO");    PyRun_SimpleString("import sys");    PyRun_SimpleString("sys.stderr = cStringIO.StringIO()");    // stuff that might raise a Python exception    boost::python::import("my_script"); // if my_script doesn't exist  } catch (boost::python::error_already_set) {    PyErr_Print();    boost::python::object sys(      boost::python::handle<>(PyImport_ImportModule("sys"))    );    boost::python::object err = sys.attr("stderr");    std::string err_text = boost::python::extract<std::string>(err.attr("getvalue")());    MessageBox(0, err_text.c_str(), "Python Error", MB_OK);  }  return 0;}

Replace windows.h/MessageBox() with your choice of message box function.
@SiCrane: Thank you so much, it works like a charme :-)

Kind Regards
NoRulez

This topic is closed to new replies.

Advertisement