Skip to main content
GameDev.net gamedev.net
🔒 Locked

boost + python + redirect PyErr_Print

Started by NoRulez Apr 23, 2008 at 12:25 AM 8 replies 9.9k views
Original Post
NoRulez
NoRulez
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]
Promethium
Promethium
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.
NoRulez
NoRulez
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
Promethium
Promethium
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 :)
NoRulez
NoRulez
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
Emmanuel77
Emmanuel77
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
Promethium
Promethium
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
NoRulez
NoRulez
@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
SiCrane
SiCrane
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.
NoRulez
NoRulez
@SiCrane: Thank you so much, it works like a charme :-)

Kind Regards
NoRulez

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.