summaryrefslogtreecommitdiffstats
path: root/Python/errors.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2023-02-08 09:31:12 (GMT)
committerGitHub <noreply@github.com>2023-02-08 09:31:12 (GMT)
commitfeec49c40736fc05626a183a8d14c4ebbea5ae28 (patch)
tree5af6110eca8c2a21a9f699b40a87e7567c603e98 /Python/errors.c
parent027adf42cd85db41fee05b0a40d89ef822876c97 (diff)
downloadcpython-feec49c40736fc05626a183a8d14c4ebbea5ae28.zip
cpython-feec49c40736fc05626a183a8d14c4ebbea5ae28.tar.gz
cpython-feec49c40736fc05626a183a8d14c4ebbea5ae28.tar.bz2
GH-101578: Normalize the current exception (GH-101607)
* Make sure that the current exception is always normalized. * Remove redundant type and traceback fields for the current exception. * Add new API functions: PyErr_GetRaisedException, PyErr_SetRaisedException * Add new API functions: PyException_GetArgs, PyException_SetArgs
Diffstat (limited to 'Python/errors.c')
-rw-r--r--Python/errors.c225
1 files changed, 141 insertions, 84 deletions
diff --git a/Python/errors.c b/Python/errors.c
index 05ef622..f573bed 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -27,32 +27,84 @@ static PyObject *
_PyErr_FormatV(PyThreadState *tstate, PyObject *exception,
const char *format, va_list vargs);
-
void
-_PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value,
- PyObject *traceback)
+_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc)
{
- PyObject *oldtype, *oldvalue, *oldtraceback;
+ PyObject *old_exc = tstate->current_exception;
+ tstate->current_exception = exc;
+ Py_XDECREF(old_exc);
+}
- if (traceback != NULL && !PyTraceBack_Check(traceback)) {
- /* XXX Should never happen -- fatal error instead? */
- /* Well, it could be None. */
- Py_SETREF(traceback, NULL);
+static PyObject*
+_PyErr_CreateException(PyObject *exception_type, PyObject *value)
+{
+ PyObject *exc;
+
+ if (value == NULL || value == Py_None) {
+ exc = _PyObject_CallNoArgs(exception_type);
+ }
+ else if (PyTuple_Check(value)) {
+ exc = PyObject_Call(exception_type, value, NULL);
+ }
+ else {
+ exc = PyObject_CallOneArg(exception_type, value);
}
- /* Save these in locals to safeguard against recursive
- invocation through Py_XDECREF */
- oldtype = tstate->curexc_type;
- oldvalue = tstate->curexc_value;
- oldtraceback = tstate->curexc_traceback;
+ if (exc != NULL && !PyExceptionInstance_Check(exc)) {
+ PyErr_Format(PyExc_TypeError,
+ "calling %R should have returned an instance of "
+ "BaseException, not %s",
+ exception_type, Py_TYPE(exc)->tp_name);
+ Py_CLEAR(exc);
+ }
- tstate->curexc_type = type;
- tstate->curexc_value = value;
- tstate->curexc_traceback = traceback;
+ return exc;
+}
- Py_XDECREF(oldtype);
- Py_XDECREF(oldvalue);
- Py_XDECREF(oldtraceback);
+void
+_PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value,
+ PyObject *traceback)
+{
+ if (type == NULL) {
+ assert(value == NULL);
+ assert(traceback == NULL);
+ _PyErr_SetRaisedException(tstate, NULL);
+ return;
+ }
+ assert(PyExceptionClass_Check(type));
+ if (value != NULL && type == (PyObject *)Py_TYPE(value)) {
+ /* Already normalized */
+ assert(((PyBaseExceptionObject *)value)->traceback != Py_None);
+ }
+ else {
+ PyObject *exc = _PyErr_CreateException(type, value);
+ Py_XDECREF(value);
+ if (exc == NULL) {
+ Py_DECREF(type);
+ Py_XDECREF(traceback);
+ return;
+ }
+ value = exc;
+ }
+ assert(PyExceptionInstance_Check(value));
+ if (traceback != NULL && !PyTraceBack_Check(traceback)) {
+ if (traceback == Py_None) {
+ Py_DECREF(Py_None);
+ traceback = NULL;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "traceback must be a Traceback or None");
+ Py_XDECREF(value);
+ Py_DECREF(type);
+ Py_XDECREF(traceback);
+ return;
+ }
+ }
+ PyObject *old_traceback = ((PyBaseExceptionObject *)value)->traceback;
+ ((PyBaseExceptionObject *)value)->traceback = traceback;
+ Py_XDECREF(old_traceback);
+ _PyErr_SetRaisedException(tstate, value);
+ Py_DECREF(type);
}
void
@@ -62,6 +114,12 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
_PyErr_Restore(tstate, type, value, traceback);
}
+void
+PyErr_SetRaisedException(PyObject *exc)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ _PyErr_SetRaisedException(tstate, exc);
+}
_PyErr_StackItem *
_PyErr_GetTopmostException(PyThreadState *tstate)
@@ -77,32 +135,6 @@ _PyErr_GetTopmostException(PyThreadState *tstate)
return exc_info;
}
-static PyObject*
-_PyErr_CreateException(PyObject *exception_type, PyObject *value)
-{
- PyObject *exc;
-
- if (value == NULL || value == Py_None) {
- exc = _PyObject_CallNoArgs(exception_type);
- }
- else if (PyTuple_Check(value)) {
- exc = PyObject_Call(exception_type, value, NULL);
- }
- else {
- exc = PyObject_CallOneArg(exception_type, value);
- }
-
- if (exc != NULL && !PyExceptionInstance_Check(exc)) {
- PyErr_Format(PyExc_TypeError,
- "calling %R should have returned an instance of "
- "BaseException, not %s",
- exception_type, Py_TYPE(exc)->tp_name);
- Py_CLEAR(exc);
- }
-
- return exc;
-}
-
void
_PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
{
@@ -117,30 +149,29 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
exception);
return;
}
-
Py_XINCREF(value);
- exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
- if (exc_value != NULL && exc_value != Py_None) {
- /* Implicit exception chaining */
- Py_INCREF(exc_value);
- if (value == NULL || !PyExceptionInstance_Check(value)) {
- /* We must normalize the value right now */
- PyObject *fixed_value;
-
- /* Issue #23571: functions must not be called with an
- exception set */
- _PyErr_Clear(tstate);
+ /* Normalize the exception */
+ if (value == NULL || (PyObject *)Py_TYPE(value) != exception) {
+ /* We must normalize the value right now */
+ PyObject *fixed_value;
- fixed_value = _PyErr_CreateException(exception, value);
- Py_XDECREF(value);
- if (fixed_value == NULL) {
- Py_DECREF(exc_value);
- return;
- }
+ /* Issue #23571: functions must not be called with an
+ exception set */
+ _PyErr_Clear(tstate);
- value = fixed_value;
+ fixed_value = _PyErr_CreateException(exception, value);
+ Py_XDECREF(value);
+ if (fixed_value == NULL) {
+ return;
}
+ value = fixed_value;
+ }
+
+ exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
+ if (exc_value != NULL && exc_value != Py_None) {
+ /* Implicit exception chaining */
+ Py_INCREF(exc_value);
/* Avoid creating new reference cycles through the
context chain, while taking care not to hang on
pre-existing ones.
@@ -414,17 +445,34 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
}
+PyObject *
+_PyErr_GetRaisedException(PyThreadState *tstate) {
+ PyObject *exc = tstate->current_exception;
+ tstate->current_exception = NULL;
+ return exc;
+}
+
+PyObject *
+PyErr_GetRaisedException(void)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ return _PyErr_GetRaisedException(tstate);
+}
+
void
_PyErr_Fetch(PyThreadState *tstate, PyObject **p_type, PyObject **p_value,
PyObject **p_traceback)
{
- *p_type = tstate->curexc_type;
- *p_value = tstate->curexc_value;
- *p_traceback = tstate->curexc_traceback;
-
- tstate->curexc_type = NULL;
- tstate->curexc_value = NULL;
- tstate->curexc_traceback = NULL;
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+ *p_value = exc;
+ if (exc == NULL) {
+ *p_type = NULL;
+ *p_traceback = NULL;
+ }
+ else {
+ *p_type = Py_NewRef(Py_TYPE(exc));
+ *p_traceback = Py_XNewRef(((PyBaseExceptionObject *)exc)->traceback);
+ }
}
@@ -597,6 +645,28 @@ _PyErr_ChainExceptions(PyObject *typ, PyObject *val, PyObject *tb)
}
}
+/* 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
+ any cycles in the exception context chain. */
+void
+_PyErr_ChainExceptions1(PyObject *exc)
+{
+ if (exc == NULL) {
+ return;
+ }
+ PyThreadState *tstate = _PyThreadState_GET();
+ if (_PyErr_Occurred(tstate)) {
+ PyObject *exc2 = _PyErr_GetRaisedException(tstate);
+ PyException_SetContext(exc2, exc);
+ _PyErr_SetRaisedException(tstate, exc2);
+ }
+ else {
+ _PyErr_SetRaisedException(tstate, exc);
+ }
+}
+
/* Set the currently set exception's context to the given exception.
If the provided exc_info is NULL, then the current Python thread state's
@@ -707,19 +777,6 @@ PyErr_BadArgument(void)
}
PyObject *
-_PyErr_NoMemory(PyThreadState *tstate)
-{
- if (Py_IS_TYPE(PyExc_MemoryError, NULL)) {
- /* PyErr_NoMemory() has been called before PyExc_MemoryError has been
- initialized by _PyExc_Init() */
- Py_FatalError("Out of memory and PyExc_MemoryError is not "
- "initialized yet");
- }
- _PyErr_SetNone(tstate, PyExc_MemoryError);
- return NULL;
-}
-
-PyObject *
PyErr_NoMemory(void)
{
PyThreadState *tstate = _PyThreadState_GET();