0

In an old question about how to catch python stdout in C++ code, there is a good answer and it works - but only in Python 2.

I would like to use something like that with Python 3. Anyone could help me here?

UPDATE

The code I am using is below. It was ported from Mark answer cited above, the only change was the use of PyBytes_AsString instead of PyString_AsString, as cited in documentation.

#include <Python.h>
#include <string>

int main(int argc, char** argv)
{
std::string stdOutErr =
"import sys\n\
class CatchOutErr:\n\
    def __init__(self):\n\
        self.value = ''\n\
    def write(self, txt):\n\
        self.value += txt\n\
catchOutErr = CatchOutErr()\n\
sys.stdout = catchOutErr\n\
sys.stderr = catchOutErr\n\
"; //this is python code to redirect stdouts/stderr

Py_Initialize();
PyObject *pModule = PyImport_AddModule("__main__"); //create main module
PyRun_SimpleString(stdOutErr.c_str()); //invoke code to redirect
PyRun_SimpleString("print(1+1)"); //this is ok stdout
PyRun_SimpleString("1+a"); //this creates an error
PyObject *catcher = PyObject_GetAttrString(pModule,"catchOutErr"); //get our catchOutErr created above
PyErr_Print(); //make python print any errors

PyObject *output = PyObject_GetAttrString(catcher,"value"); //get the stdout and stderr from our catchOutErr object

printf("Here's the output:\n %s", PyBytes_AsString(output)); //it's not in our C++ portion

Py_Finalize();


return 0;
}

I build it using Python 3 library:

g++ -I/usr/include/python3.6m -Wall -Werror -fpic code.cpp -lpython3.6m

and the output is:

Here's the output: (null)

If someone needs more information about the question, please let me know and I will try provide here.

1 Answers1

1

Your issue is that .value isn't a bytes object, it is a string (i.e. Python2 unicode) object. Therefore PyBytes_AsString fails. We can convert it to a bytes object with PyUnicode_AsEncodedString.

PyObject *output = PyObject_GetAttrString(catcher,"value"); //get the stdout and stderr from our catchOutErr
PyObject* encoded = PyUnicode_AsEncodedString(output,"utf-8","strict");
printf("Here's the output:\n %s", PyBytes_AsString(encoded));

Note that you should be checking these result PyObject* against NULL to see if an error has occurred.

DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Thanks DavidW, your tip solve the problem with the code above. I have other doubt related to this, in the code I am working looks like the`PyUnicode_AsEncodedString` is always returning `NULL`. The other functions are working (or at least they are not returning `NULL`). Do you have any idea about what it can be? – user2540800 Oct 09 '17 at 08:19
  • It means it's raised an exception. You should inspect the inspection to find out what's gone wrong. That's a little harder since you redirected stderr. It's possible an arguement other than "strict" might make it work. – DavidW Oct 09 '17 at 08:30