8000 GH-101578: Normalize the current exception by markshannon · Pull Request #101607 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

GH-101578: Normalize the current exception #101607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 8, 2023
Merged
Prev Previous commit
Next Next commit
Rename new functions and fix up docs.
  • Loading branch information
markshannon committed Feb 7, 2023
commit aa70bad5b4f8d5698f068842c8714b068cdedcd7
87 changes: 52 additions & 35 deletions Doc/c-api/exceptions.rst
8000
Original file line number Diff line number Diff line change
Expand Up @@ -400,76 +400,88 @@ Querying the error indicator
recursively in subtuples) are searched for a match.


.. c:function:: void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback)
.. c:function:: PyObject *PyErr_GetRaisedException(void)

As of 3.12, this function is deprecated. Use :c:func:`PyErr_Fetch1` instead.

Retrieve the error indicator into three variables whose addresses are passed.
If the error indicator is not set, set all three variables to ``NULL``. If it is
set, it will be cleared and you own a reference to each object retrieved. The
value and traceback object may be ``NULL`` even when the type object is not.
Returns the exception currently being raised, clearing the exception at
the same time. Do not confuse this with the exception currently being
handled which can be accessed with :c:func:`PyErr_GetHandledException`.

.. note::

This function is normally only used by code that needs to catch exceptions or
by code that needs to save and restore the error indicator temporarily, e.g.::

{
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
PyObject *exc = PyErr_GetRaisedException();

/* ... code that might produce other errors ... */

PyErr_Restore(type, value, traceback);
PyErr_SetRaisedException(exc);
}

.. versionadded:: 3.12


.. c:function:: PyObject *PyErr_Fetch1(void)
.. c:function:: void PyErr_SetRaisedException(PyObject *exc)

Returns the current error indicator, clearing the error indicator at the same time.
Sets the exception currently being raised ``exc``.
If the exception is already set, it is cleared first.

``exc`` must be a valid exception.
(Violating this rules will cause subtle problems later.)
This call consumes a reference to the ``exc`` object: you must own a
reference to that object before the call and after the call you no longer own
that reference.
(If you don't understand this, don't use this function. I warned you.)

.. note::

This function is normally only used by code that needs to catch exceptions or
by code that needs to save and restore the error indicator temporarily, e.g.::
This function is normally only used by code that needs to save and restore the
error indicator temporarily. Use :c:func:`PyErr_GetRaisedException` to save
the current exception, e.g.::

{
PyObject *exc = PyErr_Fetch1();
PyObject *exc = PyErr_GetRaisedException();

/* ... code that might produce other errors ... */

PyErr_Restore1(exc);
PyErr_SetRaisedException(exc);
}

.. versionadded:: 3.12


.. c:function:: void PyErr_Restore1(PyObject *exc)

As of 3.12, this function is deprecated. Use :c:func:`PyErr_Restore1` instead.
.. c:function:: void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback)

Set the error indicator to ``exc``.
If the error indicator is already set, it is cleared first.
As of 3.12, this function is deprecated. Use :c:func:`PyErr_GetRaisedException` instead.

``exc`` should be a valid exception.
(Violating this rules will cause subtle problems later.)
This call consumes a reference to the ``exc`` object: you must own a
reference to that object before the call and after the call you no longer own
that reference.
(If you don't understand this, don't use this function. I warned you.)
Retrieve the error indicator into three variables whose addresses are passed.
If the error indicator is not set, set all three variables to ``NULL``. If it is
set, it will be cleared and you own a reference to each object retrieved. The
value and traceback object may be ``NULL`` even when the type object is not.

.. note::

This function is normally only used by code that needs to save and restore the
error indicator temporarily. Use :c:func:`PyErr_Fetch` to save the current
error indicator.
This function is normally only used by code that needs to catch exceptions or
by code that needs to save and restore the error indicator temporarily, e.g.::

.. versionadded:: 3.12
{
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);

/* ... code that might produce other errors ... */

PyErr_Restore(type, value, traceback);
}

.. deprecated:: 3.12


.. c:function:: void PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)

Set the error indicator from the three objects. If the error indicator is
As of 3.12, this function is deprecated. Use :c:func:`PyErr_SetRaisedException` instead.

Set the error indicator from the three objects. If the error indicator is
already set, it is cleared first. If the objects are ``NULL``, the error
indicator is cleared. Do not pass a ``NULL`` type and non-``NULL`` value or
traceback. The exception type should be a class. Do not pass an invalid
Expand All @@ -482,14 +494,17 @@ Querying the error indicator
.. note::

This function is normally only used by code that needs to save and restore the
error indicator temporarily. Use :c:func:`PyErr_Fetch1` to save the current
error indicator temporarily. Use :c:func:`PyErr_Fetch` to save the current
error indicator.

.. deprecated:: 3.12


.. c:function:: void PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)

As of 3.12, this function is deprecated.
Use :c:func:`PyErr_Fetch1` instead of :c:func:`PyErr_Fetch`.
As of 3.12, this function is deprecated.
Use :c:func:`PyErr_GetRaisedException` instead of :c:func:`PyErr_Fetch` to avoid
any possible de-normalization.

Under certain circumstances, the values returned by :c:func:`PyErr_Fetch` below
can be "unnormalized", meaning that ``*exc`` is a class object but ``*val`` is
Expand All @@ -507,6 +522,8 @@ Querying the error indicator
PyException_SetTraceback(val, tb);
}

.. deprecated:: 3.12


.. c:function:: PyObject* PyErr_GetHandledException(void)

Expand Down
4 changes: 2 additions & 2 deletions Include/internal/pycore_pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ PyAPI_FUNC(void) _PyErr_Fetch(
PyObject **traceback);

extern PyObject *
_PyErr_Fetch1(PyThreadState *tstate);
_PyErr_GetRaisedException(PyThreadState *tstate);

PyAPI_FUNC(int) _PyErr_ExceptionMatches(
PyThreadState *tstate,
PyObject *exc);

void
_PyErr_Restore1(PyThreadState *tstate, PyObject *exc);
_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need "extern" too?


PyAPI_FUNC(void) _PyErr_Restore(
PyThreadState *tstate,
Expand Down
4 changes: 2 additions & 2 deletions Include/pyerrors.h
A3DB
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void);
PyAPI_FUNC(void) PyErr_Clear(void);
PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **);
PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyErr_Fetch1(void);
PyAPI_FUNC(void) PyErr_Restore1(PyObject *);
PyAPI_FUNC(PyObject *) PyErr_GetRaisedException(void);
PyAPI_FUNC(void) PyErr_SetRaisedException(PyObject *);
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000
PyAPI_FUNC(PyObject*) PyErr_GetHandledException(void);
PyAPI_FUNC(void) PyErr_SetHandledException(PyObject *);
Expand Down
2 changes: 1 addition & 1 deletion Modules/_testcapi/heaptype.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(

// Assert that the correct exception was raised
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
exc = PyErr_Fetch1();
exc = PyErr_GetRaisedException();
PyObject *args = PyException_GetArgs(exc);
if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) {
PyErr_SetString(PyExc_AssertionError,
Expand Down
4 changes: 2 additions & 2 deletions Modules/gcmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2083,9 +2083,9 @@ PyGC_Collect(void)
}
else {
gcstate->collecting = 1;
PyObject *exc = _PyErr_Fetch1(tstate);
PyObject *exc = _PyErr_GetRaisedException(tstate);
n = gc_collect_with_callback(tstate, NUM_GENERATIONS - 1);
_PyErr_Restore1(tstate, exc);
_PyErr_SetRaisedException(tstate, exc);
gcstate->collecting = 0;
}

Expand Down
4 changes: 2 additions & 2 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1667,11 +1667,11 @@ PyDict_GetItem(PyObject *op, PyObject *key)
Py_ssize_t ix; (void)ix;


PyObject *exc = _PyErr_Fetch1(tstate);
PyObject *exc = _PyErr_GetRaisedException(tstate);
ix = _Py_dict_lookup(mp, key, hash, &value);

/* Ignore any exception raised by the lookup */
_PyErr_Restore1(tstate, exc);
_PyErr_SetRaisedException(tstate, exc);


assert(ix >= 0 || value == NULL);
Expand Down
4 changes: 2 additions & 2 deletions Objects/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <Python.h>
#include <stdbool.h>
#include "pycore_ceval.h" // _Py_EnterRecursiveCall
#include "pycore_pyerrors.h" // struct _PyErr_Restore1
#include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException
#include "pycore_exceptions.h" // struct _Py_exc_state
#include "pycore_initconfig.h"
#include "pycore_object.h"
Expand Down Expand Up @@ -3263,7 +3263,7 @@ _PyErr_NoMemory(PyThreadState *tstate)
}
PyObject *err = get_memory_error(0, NULL, NULL);
if (err != NULL) {
_PyErr_Restore1(tstate, err);
_PyErr_SetRaisedException(tstate, err);
}
return NULL;
}
Expand Down
2 changes: 1 addition & 1 deletion Parser/pegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ _PyPegen_number_token(Parser *p)
if (tstate->current_exception != NULL &&
Py_TYPE(tstate->current_exception) == (PyTypeObject *)PyExc_ValueError
) {
PyObject *exc = PyErr_Fetch1();
PyObject *exc = PyErr_GetRaisedException();
/* Intentionally omitting columns to avoid a wall of 1000s of '^'s
* on the error message. Nobody is going to overlook their huge
* numeric literal once given the line. */
Expand Down
2 changes: 1 addition & 1 deletion Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ dummy_func(
DECREF_INPUTS();
}
else {
_PyErr_Restore1(tstate, Py_NewRef(exc_value));
_PyErr_SetRaisedException(tstate, Py_NewRef(exc_value));
goto exception_unwind;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2905,7 +2905,7 @@ format_kwargs_error(PyThreadState *tstate, PyObject *func, PyObject *kwargs)
}
}
else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
PyObject *exc = _PyErr_Fetch1(tstate);
PyObject *exc = _PyErr_GetRaisedException(tstate);
PyObject *args = ((PyBaseExceptionObject *)exc)->args;
if (exc && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1) {
_PyErr_Clear(tstate);
Expand All @@ -2921,7 +2921,7 @@ format_kwargs_error(PyThreadState *tstate, PyObject *func, PyObject *kwargs)
Py_XDECREF(exc);
}
else {
_PyErr_Restore1(tstate, exc);
_PyErr_SetRaisedException(tstate, exc);
}
}
}
Expand Down
20 changes: 10 additions & 10 deletions Python/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ _PyErr_FormatV(PyThreadState *tstate, PyObject *exception,
const char *format, va_list vargs);

void
_PyErr_Restore1(PyThreadState *tstate, PyObject *exc)
_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc)
{
PyObject *old_exc = tstate->current_exception;
tstate->current_exception = exc;
Expand Down Expand Up @@ -61,7 +61,7 @@ _PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value,
}
#endif

_PyErr_Restore1(tstate, value);
_PyErr_SetRaisedException(tstate, value);
Py_XDECREF(type);
Py_XDECREF(traceback);
}
Expand Down Expand Up @@ -425,24 +425,24 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)


PyObject *
_PyErr_Fetch1(PyThreadState *tstate) {
_PyErr_GetRaisedException(PyThreadState *tstate) {
PyObject *exc = tstate->current_exception;
tstate->current_exception = NULL;
return exc;
}

PyObject *
PyErr_Fetch1(void)
PyErr_GetRaisedException(void)
{
PyThreadState *tstate = _PyThreadState_GET();
return _PyErr_Fetch1(tstate);
return _PyErr_GetRaisedException(tstate);
}

void
_PyErr_Fetch(PyThreadState *tstate, PyObject **p_type, PyObject **p_value,
PyObject **p_traceback)
{
PyObject *exc = _PyErr_Fetch1(tstate);
PyObject *exc = _PyErr_GetRaisedException(tstate);
*p_value = exc;
if (exc == NULL) {
*p_type = NULL;
Expand Down Expand Up @@ -624,7 +624,7 @@ _PyErr_ChainExceptions(PyObject *typ, PyObject *val, PyObject *tb)
}
}

/* Like PyErr_Restore1(), but if an exception is already set,
/* Like PyErr_SetRaisedException(), but if an exception is already set,
set the context associated with it.

The caller is responsible for ensuring that this call won't create
Expand All @@ -637,12 +637,12 @@ _PyErr_ChainExceptions1(PyObject *exc)
}
PyThreadState *tstate = _PyThreadState_GET();
if (_PyErr_Occurred(tstate)) {
PyObject *exc2 = _PyErr_Fetch1(tstate);
PyObject *exc2 = _PyErr_GetRaisedException(tstate);
PyException_SetContext(exc2, exc);
_PyErr_Restore1(tstate, exc2);
_PyErr_SetRaisedException(tstate, exc2);
}
else {
_PyErr_Restore1(tstate, exc);
_PyErr_SetRaisedException(tstate, exc);
}
}

Expand Down
2 changes: 1 addition & 1 deletion Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -3143,7 +3143,7 @@ init_dump_ascii_wstr(const wchar_t *str)
void
_Py_DumpPathConfig(PyThreadState *tstate)
{
PyObject *exc = _PyErr_Fetch1(tstate);
PyObject *exc = _PyErr_GetRaisedException(tstate);

PySys_WriteStderr("Python path configuration:\n");

Expand Down Expand Up @@ -3201,5 +3201,5 @@ _Py_DumpPathConfig(PyThreadState *tstate)
PySys_WriteStderr(" ]\n");
}

_PyErr_Restore1(tstate, exc);
_PyErr_SetRaisedException(tstate, exc);
}
8 changes: 4 additions & 4 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ _PySys_GetAttr(PyThreadState *tstate, PyObject *name)
if (sd == NULL) {
return NULL;
}
PyObject *exc = _PyErr_Fetch1(tstate);
PyObject *exc = _PyErr_GetRaisedException(tstate);
/* XXX Suppress a new exception if it was raised and restore
* the old one. */
PyObject *value = _PyDict_GetItemWithError(sd, name);
_PyErr_Restore1(tstate, exc);
_PyErr_SetRaisedException(tstate, exc);
return value;
}

Expand Down Expand Up @@ -3706,7 +3706,7 @@ sys_format(PyObject *key, FILE *fp, const char *format, va_list va)
const char *utf8;
PyThreadState *tstate = _PyThreadState_GET();

PyObject *error = _PyErr_Fetch1(tstate);
PyObject *error = _PyErr_GetRaisedException(tstate);
file = _PySys_GetAttr(tstate, key);
message = PyUnicode_FromFormatV(format, va);
if (message != NULL) {
Expand All @@ -3718,7 +3718,7 @@ sys_format(PyObject *key, FILE *fp, const char *format, va_list va)
}
Py_DECREF(message);
}
_PyErr_Restore1(tstate, error);
_PyErr_SetRaisedException(tstate, error);
}

void
Expand Down
Loading
0