Search code examples
pythonc++cpython

CPython - print traceback


How can I convert a traceback object to its string representation in CPython? I tried this but the output is not very useful.

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

    PyObject* strTrace = PyObject_Repr(traceback);
    pyStrTrace = PyUnicode_AsEncodedString(strTrace, "utf-8", "strict");
    Py_XDECREF(strTrace);
    const char* sTrace = PyBytes_AS_STRING(pyStrTrace);

Content of sTrace:

 <traceback object at 0x000002594A5CC598>

Solution

  • One comment suggested to import traceback python module which would most certainly work well but I prefer a C++ only solution so I came up with this snippet. Hopefully, someone finds it useful.

    First include Python's <frameobject.h>

    if (PyTraceBack_Check(traceback))
    {
        PyTracebackObject* traceRoot = (PyTracebackObject*)traceback;
        PyTracebackObject* pTrace = traceRoot;
        
        while (pTrace != NULL)
        {
            PyFrameObject* frame = pTrace->tb_frame;
            PyCodeObject* code = frame->f_code;
    
            int lineNr = PyFrame_GetLineNumber(frame);
            const char *sCodeName = PyUnicode_AsUTF8(code->co_name);
            const char *sFileName = PyUnicode_AsUTF8(code->co_filename);
    
            printf("at %s (%s:%d); ", sCodeName, sFileName, lineNr);
            pTrace = pTrace->tb_next;
        }        
    }
    

    As of Python 3.11 frame->f_code is nolonger possible (PyFrameObject was moved private). It's suggested in the changelog that PyFrame_GetCode(frame) is used instead.

    Likewise, this removes the need to include <frameobject.h>

    For backwards compatibility you can implement this function yourself:

    #if PY_VERSION_HEX < 0x030900B1
    #include <frameobject.h>
    static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame)
    {
        Py_INCREF(frame->f_code);
        return frame->f_code;
    }
    #endif