diff --git a/Include/internal/pycore_pyerrors.h b/Include/internal/pycore_pyerrors.h index 0f4d41c7e0b..14ea182f4f4 100644 --- a/Include/internal/pycore_pyerrors.h +++ b/Include/internal/pycore_pyerrors.h @@ -28,6 +28,8 @@ static inline void _PyErr_ClearExcState(_PyErr_StackItem *exc_state) Py_XDECREF(tb); } +PyAPI_FUNC(PyObject*) _PyErr_StackItemToExcInfoTuple( + _PyErr_StackItem *err_info); PyAPI_FUNC(void) _PyErr_Fetch( PyThreadState *tstate, diff --git a/Python/ceval.c b/Python/ceval.c index 0aec5aa7fb9..9beb1a43682 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1102,7 +1102,7 @@ static void _assert_exception_type_is_redundant(PyObject* type, PyObject* val) { if (type == NULL || type == Py_None) { - assert(val == NULL || val == Py_None); + assert(val == type); } else { assert(PyExceptionInstance_Check(val)); @@ -3738,7 +3738,9 @@ check_eval_breaker: TARGET(JUMP_IF_NOT_EXC_MATCH) { PyObject *right = POP(); - PyObject *left = TOP(); + ASSERT_EXC_TYPE_IS_REDUNDANT(TOP(), SECOND()); + PyObject *left = SECOND(); + assert(PyExceptionInstance_Check(left)); if (check_except_type_valid(tstate, right) < 0) { Py_DECREF(right); goto error; @@ -4198,7 +4200,13 @@ check_eval_breaker: ASSERT_EXC_TYPE_IS_REDUNDANT(type, value); _PyErr_StackItem *exc_info = tstate->exc_info; SET_THIRD(exc_info->exc_traceback); - SET_SECOND(exc_info->exc_value); + if (exc_info->exc_value != NULL) { + SET_SECOND(exc_info->exc_value); + } + else { + Py_INCREF(Py_None); + SET_SECOND(Py_None); + } if (exc_info->exc_type != NULL) { SET_TOP(exc_info->exc_type); } @@ -5916,7 +5924,9 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) type = exc_info->exc_type; value = exc_info->exc_value; tb = exc_info->exc_traceback; - if (Py_IsNone(type) || type == NULL) { + 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; diff --git a/Python/errors.c b/Python/errors.c index cb3938d2085..6e74d19b78e 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -79,11 +79,16 @@ _PyErr_StackItem * _PyErr_GetTopmostException(PyThreadState *tstate) { _PyErr_StackItem *exc_info = tstate->exc_info; - while ((exc_info->exc_type == NULL || exc_info->exc_type == Py_None) && + assert(exc_info); + + while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) && exc_info->previous_item != NULL) { + assert(exc_info->exc_type == NULL || exc_info->exc_type == Py_None); exc_info = exc_info->previous_item; } + assert(exc_info->previous_item == NULL || + (exc_info->exc_type != NULL && exc_info->exc_type != Py_None)); return exc_info; } @@ -471,10 +476,20 @@ _PyErr_GetExcInfo(PyThreadState *tstate, PyObject **p_type, PyObject **p_value, PyObject **p_traceback) { _PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate); - *p_type = exc_info->exc_type; + *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); + } + Py_XINCREF(*p_type); Py_XINCREF(*p_value); Py_XINCREF(*p_traceback); @@ -507,42 +522,66 @@ PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback) Py_XDECREF(oldtraceback); } + +PyObject* +_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)); + + PyObject *exc_type = PyExceptionInstance_Check(exc_value) ? + PyExceptionInstance_Class(exc_value) : + Py_None; + + return Py_BuildValue( + "(OOO)", + exc_type, + exc_value, + err_info->exc_traceback != NULL ? + err_info->exc_traceback : Py_None); +} + + /* Like PyErr_Restore(), 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_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb) +_PyErr_ChainExceptions(PyObject *typ, PyObject *val, PyObject *tb) { - if (exc == NULL) + if (typ == NULL) return; PyThreadState *tstate = _PyThreadState_GET(); - if (!PyExceptionClass_Check(exc)) { + if (!PyExceptionClass_Check(typ)) { _PyErr_Format(tstate, PyExc_SystemError, "_PyErr_ChainExceptions: " "exception %R is not a BaseException subclass", - exc); + typ); return; } if (_PyErr_Occurred(tstate)) { - PyObject *exc2, *val2, *tb2; - _PyErr_Fetch(tstate, &exc2, &val2, &tb2); - _PyErr_NormalizeException(tstate, &exc, &val, &tb); + PyObject *typ2, *val2, *tb2; + _PyErr_Fetch(tstate, &typ2, &val2, &tb2); + _PyErr_NormalizeException(tstate, &typ, &val, &tb); if (tb != NULL) { PyException_SetTraceback(val, tb); Py_DECREF(tb); } - Py_DECREF(exc); - _PyErr_NormalizeException(tstate, &exc2, &val2, &tb2); + Py_DECREF(typ); + _PyErr_NormalizeException(tstate, &typ2, &val2, &tb2); PyException_SetContext(val2, val); - _PyErr_Restore(tstate, exc2, val2, tb2); + _PyErr_Restore(tstate, typ2, val2, tb2); } else { - _PyErr_Restore(tstate, exc, val, tb); + _PyErr_Restore(tstate, typ, val, tb); } } @@ -567,7 +606,11 @@ _PyErr_ChainStackItem(_PyErr_StackItem *exc_info) } else { exc_info_given = 1; } - if (exc_info->exc_type == NULL || exc_info->exc_type == Py_None) { + + assert( (exc_info->exc_type == NULL || exc_info->exc_type == Py_None) == + (exc_info->exc_value == NULL || exc_info->exc_value == Py_None) ); + + if (exc_info->exc_value == NULL || exc_info->exc_value == Py_None) { return; } @@ -579,21 +622,32 @@ _PyErr_ChainStackItem(_PyErr_StackItem *exc_info) tstate->exc_info = exc_info; } - PyObject *exc, *val, *tb; - _PyErr_Fetch(tstate, &exc, &val, &tb); + PyObject *typ, *val, *tb; + _PyErr_Fetch(tstate, &typ, &val, &tb); - PyObject *exc2, *val2, *tb2; - exc2 = exc_info->exc_type; + PyObject *typ2, *val2, *tb2; + typ2 = exc_info->exc_type; val2 = exc_info->exc_value; tb2 = exc_info->exc_traceback; - _PyErr_NormalizeException(tstate, &exc2, &val2, &tb2); +#ifdef Py_DEBUG + PyObject *typ2_before = typ2; + PyObject *val2_before = val2; + PyObject *tb2_before = tb2; +#endif + _PyErr_NormalizeException(tstate, &typ2, &val2, &tb2); +#ifdef Py_DEBUG + /* exc_info should already be normalized */ + assert(typ2 == typ2_before); + assert(val2 == val2_before); + assert(tb2 == tb2_before); +#endif if (tb2 != NULL) { PyException_SetTraceback(val2, tb2); } /* _PyErr_SetObject sets the context from PyThreadState. */ - _PyErr_SetObject(tstate, exc, val); - Py_DECREF(exc); // since _PyErr_Occurred was true + _PyErr_SetObject(tstate, typ, val); + Py_DECREF(typ); // since _PyErr_Occurred was true Py_XDECREF(val); Py_XDECREF(tb); diff --git a/Python/pystate.c b/Python/pystate.c index 56db095d24b..ba14c9d8af9 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1341,11 +1341,7 @@ _PyThread_CurrentExceptions(void) if (id == NULL) { goto fail; } - PyObject *exc_info = PyTuple_Pack( - 3, - err_info->exc_type != NULL ? err_info->exc_type : Py_None, - err_info->exc_value != NULL ? err_info->exc_value : Py_None, - err_info->exc_traceback != NULL ? err_info->exc_traceback : Py_None); + PyObject *exc_info = _PyErr_StackItemToExcInfoTuple(err_info); if (exc_info == NULL) { Py_DECREF(id); goto fail; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 3e2091e70ab..13fae797b29 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -785,12 +785,7 @@ sys_exc_info_impl(PyObject *module) /*[clinic end generated code: output=3afd0940cf3a4d30 input=b5c5bf077788a3e5]*/ { _PyErr_StackItem *err_info = _PyErr_GetTopmostException(_PyThreadState_GET()); - return Py_BuildValue( - "(OOO)", - err_info->exc_type != NULL ? err_info->exc_type : Py_None, - err_info->exc_value != NULL ? err_info->exc_value : Py_None, - err_info->exc_traceback != NULL ? - err_info->exc_traceback : Py_None); + return _PyErr_StackItemToExcInfoTuple(err_info); }