Merged revisions 67688 via svnmerge from
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
........
diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py
index 43f3271..b192429 100644
--- a/Lib/test/test_with.py
+++ b/Lib/test/test_with.py
@@ -505,6 +505,36 @@
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 __bool__(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):
diff --git a/Misc/NEWS b/Misc/NEWS
index cb2cf43..01a5927 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@
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 #4445: Replace "sizeof(PyBytesObject)" with
"offsetof(PyBytesObject, ob_sval) + 1" when allocating memory for
bytes instances. On a typical machine this saves 3 bytes of memory
diff --git a/Python/ceval.c b/Python/ceval.c
index c394554..84d18b3 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -2189,7 +2189,17 @@
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);
SET_TOP(PyLong_FromLong((long) WHY_SILENCED));
@@ -2197,7 +2207,6 @@
Py_DECREF(v);
Py_DECREF(w);
}
- Py_DECREF(x);
PREDICT(END_FINALLY);
break;
}