diff -r 83877018ef97 Include/pyerrors.h --- a/Include/pyerrors.h Tue Oct 18 13:27:54 2016 +0300 +++ b/Include/pyerrors.h Tue Oct 18 16:09:22 2016 +0300 @@ -255,6 +255,17 @@ PyAPI_FUNC(PyObject *) PyErr_FormatV( va_list vargs); #endif +#ifndef Py_LIMITED_API +/* Like PyErr_Format(), but saves current exception as __context__ and + __cause__. + */ +PyAPI_FUNC(PyObject *) _PyErr_FormatFromCause( + PyObject *exception, + const char *format, /* ASCII-encoded string */ + ... + ); +#endif + #ifdef MS_WINDOWS PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename( int ierr, diff -r 83877018ef97 Lib/test/test_capi.py --- a/Lib/test/test_capi.py Tue Oct 18 13:27:54 2016 +0300 +++ b/Lib/test/test_capi.py Tue Oct 18 16:09:22 2016 +0300 @@ -222,8 +222,8 @@ class CAPITest(unittest.TestCase): br'result with an error set\n' br'ValueError\n' br'\n' - br'During handling of the above exception, ' - br'another exception occurred:\n' + br'The above exception was the direct cause ' + br'of the following exception:\n' br'\n' br'SystemError: ' diff -r 83877018ef97 Modules/zipimport.c --- a/Modules/zipimport.c Tue Oct 18 13:27:54 2016 +0300 +++ b/Modules/zipimport.c Tue Oct 18 16:09:22 2016 +0300 @@ -907,10 +907,8 @@ read_directory(PyObject *archive) fp = _Py_fopen_obj(archive, "rb"); if (fp == NULL) { if (PyErr_ExceptionMatches(PyExc_OSError)) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); - PyErr_Format(ZipImportError, "can't open Zip file: %R", archive); - _PyErr_ChainExceptions(exc, val, tb); + _PyErr_FormatFromCause(ZipImportError, + "can't open Zip file: %R", archive); } return NULL; } diff -r 83877018ef97 Objects/abstract.c --- a/Objects/abstract.c Tue Oct 18 13:27:54 2016 +0300 +++ b/Objects/abstract.c Tue Oct 18 16:09:22 2016 +0300 @@ -2198,20 +2198,18 @@ PyObject* } else { if (err_occurred) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); - Py_DECREF(result); - if (func) - PyErr_Format(PyExc_SystemError, - "%R returned a result with an error set", - func); - else - PyErr_Format(PyExc_SystemError, - "%s returned a result with an error set", - where); - _PyErr_ChainExceptions(exc, val, tb); + if (func) { + _PyErr_FormatFromCause(PyExc_SystemError, + "%R returned a result with an error set", + func); + } + else { + _PyErr_FormatFromCause(PyExc_SystemError, + "%s returned a result with an error set", + where); + } #ifdef Py_DEBUG /* Ensure that the bug is caught in debug mode */ Py_FatalError("a function returned a result with an error set"); diff -r 83877018ef97 Objects/genobject.c --- a/Objects/genobject.c Tue Oct 18 13:27:54 2016 +0300 +++ b/Objects/genobject.c Tue Oct 18 16:09:22 2016 +0300 @@ -118,33 +118,6 @@ gen_dealloc(PyGenObject *gen) PyObject_GC_Del(gen); } -static void -gen_chain_runtime_error(const char *msg) -{ - PyObject *exc, *val, *val2, *tb; - - /* TODO: This about rewriting using _PyErr_ChainExceptions. */ - - PyErr_Fetch(&exc, &val, &tb); - PyErr_NormalizeException(&exc, &val, &tb); - if (tb != NULL) { - PyException_SetTraceback(val, tb); - } - - Py_DECREF(exc); - Py_XDECREF(tb); - - PyErr_SetString(PyExc_RuntimeError, msg); - PyErr_Fetch(&exc, &val2, &tb); - PyErr_NormalizeException(&exc, &val2, &tb); - - Py_INCREF(val); - PyException_SetCause(val2, val); - PyException_SetContext(val2, val); - - PyErr_Restore(exc, val2, tb); -} - static PyObject * gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) { @@ -276,8 +249,7 @@ gen_send_ex(PyGenObject *gen, PyObject * else if PyAsyncGen_CheckExact(gen) { msg = "async generator raised StopIteration"; } - /* Raise a RuntimeError */ - gen_chain_runtime_error(msg); + _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg); } else { /* `gen` is an ordinary generator without @@ -309,7 +281,7 @@ gen_send_ex(PyGenObject *gen, PyObject * raise a RuntimeError. */ const char *msg = "async generator raised StopAsyncIteration"; - gen_chain_runtime_error(msg); + _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg); } if (!result || f->f_stacktop == NULL) { diff -r 83877018ef97 Objects/unicodeobject.c --- a/Objects/unicodeobject.c Tue Oct 18 13:27:54 2016 +0300 +++ b/Objects/unicodeobject.c Tue Oct 18 16:09:22 2016 +0300 @@ -3831,13 +3831,10 @@ PyUnicode_DecodeFSDefaultAndSize(const c Py_FileSystemDefaultEncodeErrors); #ifdef MS_WINDOWS if (!res && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) { - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); - PyErr_Format(PyExc_RuntimeError, - "filesystem path bytes were not correctly encoded with '%s'. " \ + _PyErr_FormatFromCause(PyExc_RuntimeError, + "filesystem path bytes were not correctly encoded with '%s'. " "Please report this at http://bugs.python.org/issue27781", Py_FileSystemDefaultEncoding); - _PyErr_ChainExceptions(exc, val, tb); } #endif return res; diff -r 83877018ef97 Python/errors.c --- a/Python/errors.c Tue Oct 18 13:27:54 2016 +0300 +++ b/Python/errors.c Tue Oct 18 16:09:22 2016 +0300 @@ -387,6 +387,9 @@ void PyObject *exc2, *val2, *tb2; PyErr_Fetch(&exc2, &val2, &tb2); PyErr_NormalizeException(&exc, &val, &tb); + if (tb != NULL) { + PyException_SetTraceback(val, tb); + } Py_DECREF(exc); Py_XDECREF(tb); PyErr_NormalizeException(&exc2, &val2, &tb2); @@ -398,6 +401,49 @@ void } } +static PyObject * +_PyErr_FormatVFromCause(PyObject *exception, const char *format, va_list vargs) +{ + PyObject *exc, *val, *val2, *tb; + + assert(PyErr_Occurred()); + PyErr_Fetch(&exc, &val, &tb); + PyErr_NormalizeException(&exc, &val, &tb); + if (tb != NULL) { + PyException_SetTraceback(val, tb); + } + + Py_DECREF(exc); + Py_XDECREF(tb); + + assert(!PyErr_Occurred()); + PyErr_FormatV(exception, format, vargs); + PyErr_Fetch(&exc, &val2, &tb); + PyErr_NormalizeException(&exc, &val2, &tb); + + Py_INCREF(val); + PyException_SetCause(val2, val); + PyException_SetContext(val2, val); + + PyErr_Restore(exc, val2, tb); + + return NULL; +} + +PyObject * +_PyErr_FormatFromCause(PyObject *exception, const char *format, ...) +{ + va_list vargs; +#ifdef HAVE_STDARG_PROTOTYPES + va_start(vargs, format); +#else + va_start(vargs); +#endif + _PyErr_FormatVFromCause(exception, format, vargs); + va_end(vargs); + return NULL; +} + /* Convenience functions to set a type error exception and return 0 */ int