Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 67068) +++ Python/ceval.c (working copy) @@ -2337,11 +2447,19 @@ /* 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; + + 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); @@ -2354,7 +2472,6 @@ above. Let END_FINALLY do its thing */ } Py_DECREF(x); - Py_DECREF(exit_func); PREDICT(END_FINALLY); break; } Index: Lib/test/test_with.py =================================================================== --- Lib/test/test_with.py (revision 67068) +++ Lib/test/test_with.py (working copy) @@ -503,7 +503,38 @@ 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): def testWithBreak(self):