[Python-checkins] bpo-28994: PyErr_NormalizeException() no longer use C stack for recursion. (#2035)

Serhiy Storchaka webhook-mailer at python.org
Sun Nov 5 04:27:53 EST 2017


https://github.com/python/cpython/commit/cf296537f164abeacd83011239881f75f290ed31
commit: cf296537f164abeacd83011239881f75f290ed31
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2017-11-05T11:27:48+02:00
summary:

bpo-28994: PyErr_NormalizeException() no longer use C stack for recursion. (#2035)

MemoryError raised when normalizing a RecursionError raised during exception normalization now not always causes a fatal error.

files:
M Python/errors.c

diff --git a/Python/errors.c b/Python/errors.c
index 13ebb192c54..15e6ba05714 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -228,20 +228,20 @@ PyErr_ExceptionMatches(PyObject *exc)
    XXX: should PyErr_NormalizeException() also call
             PyException_SetTraceback() with the resulting value and tb?
 */
-static void
-PyErr_NormalizeExceptionEx(PyObject **exc, PyObject **val,
-                           PyObject **tb, int recursion_depth)
+void
+PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
 {
-    PyObject *type = *exc;
-    PyObject *value = *val;
-    PyObject *inclass = NULL;
-    PyObject *initial_tb = NULL;
+    int recursion_depth = 0;
+    PyObject *type, *value, *initial_tb;
 
+  restart:
+    type = *exc;
     if (type == NULL) {
         /* There was no exception, so nothing to do. */
         return;
     }
 
+    value = *val;
     /* If PyErr_SetNone() was used, the value will have been actually
        set to NULL.
     */
@@ -250,54 +250,52 @@ PyErr_NormalizeExceptionEx(PyObject **exc, PyObject **val,
         Py_INCREF(value);
     }
 
-    if (PyExceptionInstance_Check(value))
-        inclass = PyExceptionInstance_Class(value);
-
     /* Normalize the exception so that if the type is a class, the
        value will be an instance.
     */
     if (PyExceptionClass_Check(type)) {
-        int is_subclass;
-        if (inclass) {
+        PyObject *inclass = NULL;
+        int is_subclass = 0;
+
+        if (PyExceptionInstance_Check(value)) {
+            inclass = PyExceptionInstance_Class(value);
             is_subclass = PyObject_IsSubclass(inclass, type);
-            if (is_subclass < 0)
-                goto finally;
+            if (is_subclass < 0) {
+                goto error;
+            }
         }
-        else
-            is_subclass = 0;
 
-        /* if the value was not an instance, or is not an instance
+        /* If the value was not an instance, or is not an instance
            whose class is (or is derived from) type, then use the
            value as an argument to instantiation of the type
            class.
         */
-        if (!inclass || !is_subclass) {
-            PyObject *fixed_value;
-
-            fixed_value = _PyErr_CreateException(type, value);
+        if (!is_subclass) {
+            PyObject *fixed_value = _PyErr_CreateException(type, value);
             if (fixed_value == NULL) {
-                goto finally;
+                goto error;
             }
-
             Py_DECREF(value);
             value = fixed_value;
         }
-        /* if the class of the instance doesn't exactly match the
-           class of the type, believe the instance
+        /* If the class of the instance doesn't exactly match the
+           class of the type, believe the instance.
         */
         else if (inclass != type) {
+            Py_INCREF(inclass);
             Py_DECREF(type);
             type = inclass;
-            Py_INCREF(type);
         }
     }
     *exc = type;
     *val = value;
     return;
-finally:
+
+  error:
     Py_DECREF(type);
     Py_DECREF(value);
-    if (recursion_depth + 1 == Py_NORMALIZE_RECURSION_LIMIT) {
+    recursion_depth++;
+    if (recursion_depth == Py_NORMALIZE_RECURSION_LIMIT) {
         PyErr_SetString(PyExc_RecursionError, "maximum recursion depth "
                         "exceeded while normalizing an exception");
     }
@@ -307,16 +305,18 @@ PyErr_NormalizeExceptionEx(PyObject **exc, PyObject **val,
     */
     initial_tb = *tb;
     PyErr_Fetch(exc, val, tb);
+    assert(*exc != NULL);
     if (initial_tb != NULL) {
         if (*tb == NULL)
             *tb = initial_tb;
         else
             Py_DECREF(initial_tb);
     }
-    /* Normalize recursively.
-     * Abort when Py_NORMALIZE_RECURSION_LIMIT has been exceeded and the
-     * corresponding RecursionError could not be normalized.*/
-    if (++recursion_depth > Py_NORMALIZE_RECURSION_LIMIT) {
+    /* Abort when Py_NORMALIZE_RECURSION_LIMIT has been exceeded, and the
+       corresponding RecursionError could not be normalized, and the
+       MemoryError raised when normalize this RecursionError could not be
+       normalized. */
+    if (recursion_depth >= Py_NORMALIZE_RECURSION_LIMIT + 2) {
         if (PyErr_GivenExceptionMatches(*exc, PyExc_MemoryError)) {
             Py_FatalError("Cannot recover from MemoryErrors "
                           "while normalizing exceptions.");
@@ -326,13 +326,7 @@ PyErr_NormalizeExceptionEx(PyObject **exc, PyObject **val,
                           "of an exception.");
         }
     }
-    PyErr_NormalizeExceptionEx(exc, val, tb, recursion_depth);
-}
-
-void
-PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
-{
-    PyErr_NormalizeExceptionEx(exc, val, tb, 0);
+    goto restart;
 }
 
 



More information about the Python-checkins mailing list