]> granicus.if.org Git - python/commitdiff
Merged revisions 67688 via svnmerge from
authorAmaury Forgeot d'Arc <amauryfa@gmail.com>
Wed, 10 Dec 2008 23:56:33 +0000 (23:56 +0000)
committerAmaury Forgeot d'Arc <amauryfa@gmail.com>
Wed, 10 Dec 2008 23:56:33 +0000 (23:56 +0000)
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r67688 | amaury.forgeotdarc | 2008-12-11 00:22:49 +0100 (jeu., 11 déc. 2008) | 6 lines

  #4559: When a context manager's __exit__() method returns an object whose
  conversion to bool raises an exception, 'with' loses that exception.

  Reviewed by Jeffrey Yasskin.
  Already ported to 2.5, will port to 2.6 and 3.0
........

Lib/test/test_with.py
Misc/NEWS
Python/ceval.c

index 3007e5aa2c7a3e99469fe41b89d6c567667916e0..bfeb06bdd9baa539c07e0ed4be6e1f18831bdb4b 100644 (file)
@@ -503,6 +503,36 @@ class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin):
 
         self.assertRaises(GeneratorExit, shouldThrow)
 
+    def testErrorsInBool(self):
+        # issue4589: __exit__ return code may raise an exception
+        # when looking at its truth value.
+
+        class cm(object):
+            def __init__(self, bool_conversion):
+                class Bool:
+                    def __nonzero__(self):
+                        return bool_conversion()
+                self.exit_result = Bool()
+            def __enter__(self):
+                return 3
+            def __exit__(self, a, b, c):
+                return self.exit_result
+
+        def trueAsBool():
+            with cm(lambda: True):
+                self.fail("Should NOT see this")
+        trueAsBool()
+
+        def falseAsBool():
+            with cm(lambda: False):
+                self.fail("Should raise")
+        self.assertRaises(AssertionError, falseAsBool)
+
+        def failAsBool():
+            with cm(lambda: 1//0):
+                self.fail("Should NOT see this")
+        self.assertRaises(ZeroDivisionError, failAsBool)
+
 
 class NonLocalFlowControlTestCase(unittest.TestCase):
 
index effcf2064a84c186afb3fd18e6bb993a37ebd851..f1bc34fc46e7d036f0286e1eba56d5c762959e54 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.6.2
 Core and Builtins
 -----------------
 
+- Issue #4597: Fixed exception handling when the __exit__ function of a
+  context manager returns a value that cannot be converted to a bool.
+
 - Issue #4233: Changed semantic of ``_fileio.FileIO``'s ``close()`` 
   method on file objects with closefd=False. The file descriptor is still
   kept open but the file object behaves like a closed file. The ``FileIO``
index 6eef7efa84b11ab40a4692548c670b8e00ed3898..dd1e171770f6dfb45e0d8c87221ab98a6a23446f 100644 (file)
@@ -2337,11 +2337,20 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
                        /* XXX Not the fastest way to call it... */
                        x = PyObject_CallFunctionObjArgs(exit_func, u, v, w,
                                                         NULL);
-                       if (x == NULL) {
-                               Py_DECREF(exit_func);
+                       Py_DECREF(exit_func);
+                       if (x == NULL)
                                break; /* Go to error exit */
-                       }
-                       if (u != Py_None && PyObject_IsTrue(x)) {
+
+                       if (u != Py_None)
+                               err = PyObject_IsTrue(x);
+                       else
+                               err = 0;
+                       Py_DECREF(x);
+
+                       if (err < 0)
+                               break; /* Go to error exit */
+                       else if (err > 0) {
+                               err = 0;
                                /* There was an exception and a true return */
                                STACKADJ(-2);
                                Py_INCREF(Py_None);
@@ -2353,8 +2362,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
                                /* The stack was rearranged to remove EXIT
                                   above. Let END_FINALLY do its thing */
                        }
-                       Py_DECREF(x);
-                       Py_DECREF(exit_func);
                        PREDICT(END_FINALLY);
                        break;
                }