0

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);
}
2
  • The documentation says Use PyErr_GetRaisedException() instead, to avoid any possible de-normalization. So I'd say yes, it's normalized. I guess you can check it with isinstance(pValue, pExceptionType). Commented Aug 29, 2024 at 8:39
  • The second block of code seems to not be conditional on any exception being raised. Commented Aug 29, 2024 at 8:40

1 Answer 1

0

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.

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

3 Comments

Is there any rational for why PyErr_Fetch used to work that way, and why PyErr_NormalizeException was necessary to begin with?
I was curious too and had a look at history – added that to my answer.
Neat, thanks for looking into it

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.