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.

3
  • In what way doesn't this work? The a pure Python version works fine with Python 3, so I don't see why the C-API version won't? Commented Oct 8, 2017 at 15:48
  • I will edit the question and put the code I am using. Commented Oct 8, 2017 at 15:52
  • The indentation looks wrong in stdOutErr. That'd be my first guess Commented Oct 8, 2017 at 16:04

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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 thePyUnicode_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?
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.