C/C++ Python exception traceback not being generated

1.2k views Asked by At

I recently upgraded a C++ project I'm working on, which embedds python, from Python 3.4.3 to Python 3.5.2 (it is an executable that builds as 32 or 64 bits, and has the same behavior in both versions).

I am creating my own exception:

static PyObject* MyException_type_obj = 0;

void setup(PyObject* module){
   MyException_type_obj = PyErr_NewException("my_module.MyException", NULL, NULL);

   PyObject* dict = PyModule_GetDict(module);
   PyDict_SetItemString(dict, "MyException", MyException_type_obj);
}

Then raising it in some C++ code that is called by python code:

static PyObject* RaiseIt(PyObject* self, PyObject* args)
{
   PyErr_Format(Forever_Exception_type_obj, "Exit Requested");
   return 0;
}

So far, so good. If I catch it in python, everything is fine:

try:
    my_module.raise_it()
except my_module.MyException:
    import sys, traceback
    exc_type, exc_value, exc_tb = sys.exc_info()
    print(traceback.format_exception(exc_type, exc_value, exc_tb)

Prints the correct tracback to the command line.

However, when I try to do a similar thing in C++/Python where I use PyErr_Fetch:

std::string get_traceback(){
   PyObject* type;
   PyObject* value;
   PyObject* traceback;

   PyErr_Fetch(&type, &value, &traceback);

   std::string fcn = "";
   fcn += "def get_pretty_traceback(exc_type, exc_value, exc_tb):\n";
   fcn += "    import sys, traceback\n";
   fcn += "    lines = []\n"; 
   fcn += "    lines = traceback.format_exception(exc_type, exc_value, exc_tb)\n";
   fcn += "    output = '\\n'.join(lines)\n";
   fcn += "    return output\n";

   PyRun_SimpleString(fcn.c_str());
   PyObject* mod = PyImport_ImportModule("__main__");
   PyObject* method = PyObject_GetAttrString(mod, "get_pretty_traceback");
   PyObject* outStr = PyObject_CallObject(method, Py_BuildValue("OOO", type, value, traceback));
   std::string pretty = PyBytes_AsString(PyUnicode_AsASCIIString(outStr));

   Py_DECREF(method);
   Py_DECREF(outStr);

   return pretty;
}

It breaks down and I get a null outStr.

Getting some additional details

   fcn += "    lines = []\n"; 
   fcn += "    try:\n";
   fcn += "        lines = traceback.format_exception(exc_type, exc_value, exc_tb)\n";
   fcn += "    except Exception as e:\n";
   fcn += "        print('Exception while rendering a python exception for C')\n";
   fcn += "        print(e)\n";
   fcn += "    output = '\\n'.join(lines)\n";

Gives me the cryptic error message:

'str' object has no attribute '__cause__'

As an aside, this does not happen in python 2.7 either (with minor string-related changes). It is only occouring for me in python 3.5.

This same C++ get_traceback() function works fine with exceptions initiated in python, just not the C++ ones.

1

There are 1 answers

0
teeks99 On

Apparently PyErr_NormalizeException is needed immediately after my PyErr_Fetch call, which converts the value from a string to an instance of the exception object.

PyObject* type;
PyObject* value;
PyObject* traceback;

PyErr_Fetch(&type, &value, &traceback);
PyErr_NormalizeException(&type, &value, &traceback); // Added line

...

Fixes the issue. I have no idea why the value coming from an exception generated in C++ vs. one from python changes the value, but this takes care of it.