Sign in to follow this  

boost + python + redirect PyErr_Print

This topic is 3526 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites
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.exe

This 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

Share this post


Link to post
Share on other sites
This is what I'm doing :

I have this python file 'Erreur.py':
import sys, PG_Intern_Error
RedirectStdout = 1

class 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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
@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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

This topic is 3526 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this