diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h index cdd052026c..f1a0bcf6ad 100644 --- a/Include/cpython/pyerrors.h +++ b/Include/cpython/pyerrors.h @@ -77,6 +77,8 @@ typedef PyOSErrorObject PyWindowsErrorObject; PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *); _PyErr_StackItem *_PyErr_GetTopmostException(PyThreadState *tstate); PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, PyObject **); +PyAPI_FUNC(void) _PyErr_SetExcInfo(PyThreadState *, PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(void) _PyErr_SwapExcInfo(PyThreadState *, PyObject **, PyObject **, PyObject **); /* Context manipulation (PEP 3134) */ diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 399bb7c3a6..228516d74c 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -20,6 +20,9 @@ PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(void) PyErr_GetExcInfo(PyObject **, PyObject **, PyObject **); PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *); #endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 +PyAPI_FUNC(void) PyErr_SwapExcInfo(PyObject **, PyObject **, PyObject **); +#endif /* Defined in Python/pylifecycle.c diff --git a/Objects/typeobject.c b/Objects/typeobject.c index c2ddc162ac..1e9c0f4099 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6745,8 +6745,15 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name) Py_DECREF(getattribute); } if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); + PyObject *exc, *val, *tb; + + /* push exception handling context */ + PyErr_SwapExcInfo(&exc, &val, &tb); + res = call_attribute(self, getattr, name); + + /* pop exception handling context */ + PyErr_SetExcInfo(exc, val, tb); } Py_DECREF(getattr); return res; diff --git a/Python/errors.c b/Python/errors.c index 9e53d05041..924272f149 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -456,10 +456,10 @@ PyErr_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback) } void -PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback) +_PyErr_SetExcInfo(PyThreadState *tstate, + PyObject *p_type, PyObject *p_value, PyObject *p_traceback) { PyObject *oldtype, *oldvalue, *oldtraceback; - PyThreadState *tstate = _PyThreadState_GET(); oldtype = tstate->exc_info->exc_type; oldvalue = tstate->exc_info->exc_value; @@ -474,6 +474,46 @@ PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback) Py_XDECREF(oldtraceback); } + +void +PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback) +{ + PyThreadState *tstate = _PyThreadState_GET(); + _PyErr_SetExcInfo(tstate, p_type, p_value, p_traceback); +} + + +/* Set current exception as being handled. This is just like entering + an except clause of a try-except. Remember to restore the old + exception info when leaving the clause. + */ +void +_PyErr_SwapExcInfo(PyThreadState *tstate, + PyObject **oldexc, PyObject **oldval, PyObject **oldtb) +{ + PyObject *exc, *val, *tb; + + _PyErr_GetExcInfo(tstate, oldexc, oldval, oldtb); + + _PyErr_Fetch(tstate, &exc, &val, &tb); + /* make the exception data available to exception handlers */ + _PyErr_NormalizeException(tstate, &exc, &val, &tb); + if (tb != NULL) + PyException_SetTraceback(val, tb); + else + PyException_SetTraceback(val, Py_None); + _PyErr_SetExcInfo(tstate, exc, val, tb); +} + + +void +PyErr_SwapExcInfo(PyObject **oldexc, PyObject **oldval, PyObject **oldtb) +{ + PyThreadState *tstate = _PyThreadState_GET(); + _PyErr_SwapExcInfo(tstate, oldexc, oldval, oldtb); +} + + /* Like PyErr_Restore(), but if an exception is already set, set the context associated with it. */