user17562396
user17562396

Reputation: 13

C++ Python/C API - Is exception returned by PyErr_GetRaisedException normalized?

I'm using a new Python/C API PyErr_GetRaisedException added in 3.12 version to retrieve exception and then using other Python/C APIs to get exception type, value, and traceback details.

Then I'm calling "format_exception" function from traceback module to get error call stack. But this function call throws exception

Is the exception returned by PyErr_GetRaisedException normalized??

Also, I looked at PyErr_NormalizeException API but it is deprecated in 3.12. Is there any other API to normalize exception?

PyObject* pException = PyErr_GetRaisedException();
if (pException) {
    pExceptionType = PyObject_Type(pException);
    pValue = PyException_GetArgs(pException);
    pTraceback = PyException_GetTraceback(pException);
}
PyObject* module_name = PyString_FromString("traceback");
PyObject* pyth_module = PyImport_Import(module_name);

PyObject* pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
if (pyth_func && PyCallable_Check(pyth_func)) {
    PyObject* pyArgsTuple = PyTuple_New(3);
    PyTuple_SetItem(pyArgsTuple, 0, pType);
    PyTuple_SetItem(pyArgsTuple, 1, pValue);
    PyTuple_SetItem(pyArgsTuple, 2, pTraceback);

    PyObject* pyth_val = PyObject_CallObject(pyth_func, pyArgsTuple);
    PyObject* pystr = PyObject_Str(pyth_val);
    char* traceBackCStr = PyString_AsString(pystr);
}

Upvotes: 0

Views: 108

Answers (1)

Chronial
Chronial

Reputation: 70693

Yes, the exception is normalized, because it can't be denormalized.

Background: The (also deprecated) function PyErr_Fetch returns three separate values: type, value, traceback. It does not guarantee that the type is actually the type of value. PyErr_NormalizeException fixes that.

Since PyErr_GetRaisedException only returns the value, there is no place for there to be an inconsistency. PyObject_Type always returns the correct type of value.


Historical Background (note: this is only inferred from the source code history)

In the past, PyErr_SetObject(PyObject *type, PyObject *value) directly set type and value and didn't care whether value was actually an instance of type. This was used deliberately to set an exception type and a string as a value. No exception object was actually constructed at this point. I assume this was a performance optimization.

Nowadays, PyErr_SetObject already costructs/normalizes the exception before setting it.

Upvotes: 0

Related Questions