to an exception that was *already caught*, not to an exception that was
freshly raised. This function steals the references of the arguments.
To clear the exception state, pass ``NULL`` for all three arguments.
- For general rules about the three arguments, see :c:func:`PyErr_Restore`.
.. note::
.. versionadded:: 3.3
+ .. versionchanged:: 3.11
+ The ``type`` and ``traceback`` arguments are no longer used and
+ can be NULL. The interpreter now derives them from the exception
+ instance (the ``value`` argument). The function still steals
+ references of all three arguments.
+
Signal Handling
===============
``(type, value, traceback)``. Their meaning is: *type* gets the type of the
exception being handled (a subclass of :exc:`BaseException`); *value* gets
the exception instance (an instance of the exception type); *traceback* gets
- a :ref:`traceback object <traceback-objects>` which encapsulates the call
- stack at the point where the exception originally occurred.
-
+ a :ref:`traceback object <traceback-objects>` which typically encapsulates
+ the call stack at the point where the exception last occurred.
+
+ .. versionchanged:: 3.11
+ The ``type`` and ``traceback`` fields are now derived from the ``value``
+ (the exception instance), so when an exception is modified while it is
+ being handled, the changes are reflected in the results of subsequent
+ calls to :func:`exc_info`.
.. data:: exec_prefix
The ``__suppress_context__`` attribute to suppress automatic display of the
exception context.
+.. versionchanged:: 3.11
+ If the traceback of the active exception is modified in an :keyword:`except`
+ clause, a subsequent ``raise`` statement re-raises the exception with the
+ modified traceback. Previously, the exception was re-raised with the
+ traceback it had when it was caught.
+
.. _break:
The :keyword:`!break` statement
hash-based pyc files now use ``siphash13``, too.
(Contributed by Inada Naoki in :issue:`29410`.)
+* When an active exception is re-raised by a :keyword:`raise` statement with no parameters,
+ the traceback attached to this exception is now always ``sys.exc_info()[1].__traceback__``.
+ This means that changes made to the traceback in the current :keyword:`except` clause are
+ reflected in the re-raised exception.
+ (Contributed by Irit Katriel in :issue:`45711`.)
+
New Modules
===========
(Contributed by Erlend E. Aasland in :issue:`45828`.)
+sys
+---
+
+* :func:`sys.exc_info` now derives the ``type`` and ``traceback`` fields
+ from the ``value`` (the exception instance), so when an exception is
+ modified while it is being handled, the changes are reflected in
+ the results of subsequent calls to :func:`exc_info`.
+ (Contributed by Irit Katriel in :issue:`45711`.)
+
+
threading
---------
suspend and resume tracing and profiling.
(Contributed by Victor Stinner in :issue:`43760`.)
+* :c:func:`PyErr_SetExcInfo()` no longer uses the ``type`` and ``traceback``
+ arguments, the interpreter now derives those values from the exception
+ instance (the ``value`` argument). The function still steals references
+ of all three arguments.
+ (Contributed by Irit Katriel in :issue:`45711`.)
+
+* :c:func:`PyErr_GetExcInfo()` now derives the ``type`` and ``traceback``
+ fields of the result from the exception instance (the ``value`` field).
+ (Contributed by Irit Katriel in :issue:`45711`.)
+
+
Porting to Python 3.11
----------------------
--- /dev/null
+The three values of ``exc_info`` are now always consistent with each other.
+In particular, the ``type`` and ``traceback`` fields are now derived from
+the exception instance. This impacts the return values of :func:`sys.exc_info`
+and :c:func:`PyErr_GetExcInfo()` if the exception instance is modified while
+the exception is handled, as well as :c:func:`PyErr_SetExcInfo()`, which now
+ignores the ``type`` and ``traceback`` arguments provided to it.
if (exc == NULL) {
/* Reraise */
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
- PyObject *tb;
- type = exc_info->exc_type;
value = exc_info->exc_value;
- tb = exc_info->exc_traceback;
- assert(((Py_IsNone(value) || value == NULL)) ==
- ((Py_IsNone(type) || type == NULL)));
if (Py_IsNone(value) || value == NULL) {
_PyErr_SetString(tstate, PyExc_RuntimeError,
"No active exception to reraise");
return 0;
}
+ assert(PyExceptionInstance_Check(value));
+ type = PyExceptionInstance_Class(value);
Py_XINCREF(type);
Py_XINCREF(value);
- Py_XINCREF(tb);
+ PyObject *tb = PyException_GetTraceback(value); /* new ref */
_PyErr_Restore(tstate, type, value, tb);
return 1;
}
_PyErr_Clear(tstate);
}
+static PyObject*
+get_exc_type(PyObject *exc_value) /* returns a borrowed ref */
+{
+ if (exc_value == NULL || exc_value == Py_None) {
+ return Py_None;
+ }
+ else {
+ assert(PyExceptionInstance_Check(exc_value));
+ PyObject *type = PyExceptionInstance_Class(exc_value);
+ assert(type != NULL);
+ return type;
+ }
+}
+
+static PyObject*
+get_exc_traceback(PyObject *exc_value) /* returns a borrowed ref */
+{
+ if (exc_value == NULL || exc_value == Py_None) {
+ return Py_None;
+ }
+ else {
+ assert(PyExceptionInstance_Check(exc_value));
+ PyObject *tb = PyException_GetTraceback(exc_value);
+ Py_XDECREF(tb);
+ return tb ? tb : Py_None;
+ }
+}
void
_PyErr_GetExcInfo(PyThreadState *tstate,
{
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
+ *p_type = get_exc_type(exc_info->exc_value);
*p_value = exc_info->exc_value;
- *p_traceback = exc_info->exc_traceback;
-
- if (*p_value == NULL || *p_value == Py_None) {
- assert(exc_info->exc_type == NULL || exc_info->exc_type == Py_None);
- *p_type = Py_None;
- }
- else {
- assert(PyExceptionInstance_Check(*p_value));
- assert(exc_info->exc_type == PyExceptionInstance_Class(*p_value));
- *p_type = PyExceptionInstance_Class(*p_value);
- }
+ *p_traceback = get_exc_traceback(exc_info->exc_value);
Py_XINCREF(*p_type);
Py_XINCREF(*p_value);
}
void
-PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback)
+PyErr_SetExcInfo(PyObject *type, PyObject *value, PyObject *traceback)
{
PyObject *oldtype, *oldvalue, *oldtraceback;
PyThreadState *tstate = _PyThreadState_GET();
oldvalue = tstate->exc_info->exc_value;
oldtraceback = tstate->exc_info->exc_traceback;
- tstate->exc_info->exc_type = p_type;
- tstate->exc_info->exc_value = p_value;
- tstate->exc_info->exc_traceback = p_traceback;
+
+ tstate->exc_info->exc_type = get_exc_type(value);
+ Py_XINCREF(tstate->exc_info->exc_type);
+ tstate->exc_info->exc_value = value;
+ tstate->exc_info->exc_traceback = get_exc_traceback(value);
+ Py_XINCREF(tstate->exc_info->exc_traceback);
+
+ /* These args are no longer used, but we still need to steal a ref */
+ Py_XDECREF(type);
+ Py_XDECREF(traceback);
Py_XDECREF(oldtype);
Py_XDECREF(oldvalue);
_PyErr_StackItemToExcInfoTuple(_PyErr_StackItem *err_info)
{
PyObject *exc_value = err_info->exc_value;
- if (exc_value == NULL) {
- exc_value = Py_None;
- }
- assert(exc_value == Py_None || PyExceptionInstance_Check(exc_value));
+ assert(exc_value == NULL ||
+ exc_value == Py_None ||
+ PyExceptionInstance_Check(exc_value));
- PyObject *exc_type = PyExceptionInstance_Check(exc_value) ?
- PyExceptionInstance_Class(exc_value) :
- Py_None;
+ PyObject *exc_type = get_exc_type(exc_value);
+ PyObject *exc_traceback = get_exc_traceback(exc_value);
return Py_BuildValue(
"(OOO)",
- exc_type,
- exc_value,
- err_info->exc_traceback != NULL ?
- err_info->exc_traceback : Py_None);
+ exc_type ? exc_type : Py_None,
+ exc_value ? exc_value : Py_None,
+ exc_traceback ? exc_traceback : Py_None);
}