]> granicus.if.org Git - python/commitdiff
Issue #28410: Added _PyErr_FormatFromCause() -- the helper for raising
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 21 Oct 2016 14:09:17 +0000 (17:09 +0300)
committerSerhiy Storchaka <storchaka@gmail.com>
Fri, 21 Oct 2016 14:09:17 +0000 (17:09 +0300)
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]

Include/pyerrors.h
Lib/test/test_capi.py
Modules/zipimport.c
Objects/abstract.c
Objects/genobject.c
Objects/unicodeobject.c
Python/errors.c

index 03cee3d2ba411d89fba990f02b8c996eaac91212..f9f74c0ba21a1ce1d42794537b3ef3a1496b767e 100644 (file)
@@ -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,
index d4faeb375e46105323b164e7915cf334f1ef5320..6c3625d4879138a672cc714e304eb0a5c8ef60f5 100644 (file)
@@ -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: <built-in '
                                 br'function return_result_with_error> '
index 7473a8fe8778756bcba2fab3eec9e2c2c72d5c77..59046aaae41f51240baa94e16c749ec539629280 100644 (file)
@@ -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;
     }
index 747eda076b2068134c38c7fe53221ae36603869c..f9afece815b15d6c2822ee457e78b47d07d76264 100644 (file)
@@ -2198,20 +2198,18 @@ _Py_CheckFunctionResult(PyObject *func, PyObject *result, const char *where)
     }
     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");
index 7a1e9fd6ab6bf926246d20c2166cf185cee73305..7bcf016c34cb3abf6e1cd5d1af28ad6a199a02ca 100644 (file)
@@ -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 *arg, int exc, int closing)
             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 *arg, int exc, int closing)
            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) {
index adeec8cca60fabe89a07a0ccb7982e3190acee53..80e6cf26f8243b1df3581d662fc60a3e8c047d87 100644 (file)
@@ -3835,13 +3835,10 @@ PyUnicode_DecodeFSDefaultAndSize(const char *s, Py_ssize_t size)
                                 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;
index 12bde287df57349b087ae75743761b769e8110e3..918f4dffab275df4509c510f35e757f8d509a8e5 100644 (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 */
 
 int