#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
This commit is contained in:
Amaury Forgeot d'Arc 2008-12-10 23:22:49 +00:00
parent ffd42cf444
commit ad9b5992e3
3 changed files with 46 additions and 6 deletions

View 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):

View file

@ -12,6 +12,9 @@ What's New in Python 2.7 alpha 1
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 #4597: Fixed several opcodes that weren't always propagating
exceptions.

View file

@ -2349,11 +2349,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);
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);
@ -2365,8 +2374,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;
}