Issue #28410: Added _PyErr_FormatFromCause() -- the helper for raising

new exception with setting current exception as __cause__.

_PyErr_FormatFromCause(exception, format, args...) is equivalent to Python

    raise exception(format % args) from sys.exc_info()[1]
This commit is contained in:
Serhiy Storchaka 2016-10-21 17:09:17 +03:00
parent b0426cd8c4
commit 467ab194fc
7 changed files with 70 additions and 53 deletions

View File

@ -255,6 +255,17 @@ PyAPI_FUNC(PyObject *) PyErr_FormatV(
va_list vargs); va_list vargs);
#endif #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 #ifdef MS_WINDOWS
PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename( PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename(
int ierr, int ierr,

View File

@ -222,8 +222,8 @@ class CAPITest(unittest.TestCase):
br'result with an error set\n' br'result with an error set\n'
br'ValueError\n' br'ValueError\n'
br'\n' br'\n'
br'During handling of the above exception, ' br'The above exception was the direct cause '
br'another exception occurred:\n' br'of the following exception:\n'
br'\n' br'\n'
br'SystemError: <built-in ' br'SystemError: <built-in '
br'function return_result_with_error> ' br'function return_result_with_error> '

View File

@ -907,10 +907,8 @@ read_directory(PyObject *archive)
fp = _Py_fopen_obj(archive, "rb"); fp = _Py_fopen_obj(archive, "rb");
if (fp == NULL) { if (fp == NULL) {
if (PyErr_ExceptionMatches(PyExc_OSError)) { if (PyErr_ExceptionMatches(PyExc_OSError)) {
PyObject *exc, *val, *tb; _PyErr_FormatFromCause(ZipImportError,
PyErr_Fetch(&exc, &val, &tb); "can't open Zip file: %R", archive);
PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);
_PyErr_ChainExceptions(exc, val, tb);
} }
return NULL; return NULL;
} }

View File

@ -2198,20 +2198,18 @@ _Py_CheckFunctionResult(PyObject *func, PyObject *result, const char *where)
} }
else { else {
if (err_occurred) { if (err_occurred) {
PyObject *exc, *val, *tb;
PyErr_Fetch(&exc, &val, &tb);
Py_DECREF(result); Py_DECREF(result);
if (func) if (func) {
PyErr_Format(PyExc_SystemError, _PyErr_FormatFromCause(PyExc_SystemError,
"%R returned a result with an error set", "%R returned a result with an error set",
func); func);
else }
PyErr_Format(PyExc_SystemError, else {
"%s returned a result with an error set", _PyErr_FormatFromCause(PyExc_SystemError,
where); "%s returned a result with an error set",
_PyErr_ChainExceptions(exc, val, tb); where);
}
#ifdef Py_DEBUG #ifdef Py_DEBUG
/* Ensure that the bug is caught in debug mode */ /* Ensure that the bug is caught in debug mode */
Py_FatalError("a function returned a result with an error set"); Py_FatalError("a function returned a result with an error set");

View File

@ -118,33 +118,6 @@ gen_dealloc(PyGenObject *gen)
PyObject_GC_Del(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 * static PyObject *
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
{ {
@ -276,8 +249,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
else if PyAsyncGen_CheckExact(gen) { else if PyAsyncGen_CheckExact(gen) {
msg = "async generator raised StopIteration"; msg = "async generator raised StopIteration";
} }
/* Raise a RuntimeError */ _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
gen_chain_runtime_error(msg);
} }
else { else {
/* `gen` is an ordinary generator without /* `gen` is an ordinary generator without
@ -309,7 +281,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
raise a RuntimeError. raise a RuntimeError.
*/ */
const char *msg = "async generator raised StopAsyncIteration"; const char *msg = "async generator raised StopAsyncIteration";
gen_chain_runtime_error(msg); _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
} }
if (!result || f->f_stacktop == NULL) { if (!result || f->f_stacktop == NULL) {

View File

@ -3835,13 +3835,10 @@ PyUnicode_DecodeFSDefaultAndSize(const char *s, Py_ssize_t size)
Py_FileSystemDefaultEncodeErrors); Py_FileSystemDefaultEncodeErrors);
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (!res && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) { if (!res && PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)) {
PyObject *exc, *val, *tb; _PyErr_FormatFromCause(PyExc_RuntimeError,
PyErr_Fetch(&exc, &val, &tb); "filesystem path bytes were not correctly encoded with '%s'. "
PyErr_Format(PyExc_RuntimeError,
"filesystem path bytes were not correctly encoded with '%s'. " \
"Please report this at http://bugs.python.org/issue27781", "Please report this at http://bugs.python.org/issue27781",
Py_FileSystemDefaultEncoding); Py_FileSystemDefaultEncoding);
_PyErr_ChainExceptions(exc, val, tb);
} }
#endif #endif
return res; return res;

View File

@ -401,6 +401,47 @@ _PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb)
} }
} }
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(tb);
}
Py_DECREF(exc);
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 */ /* Convenience functions to set a type error exception and return 0 */
int int