Commit 565d7858 authored by Benjamin Peterson's avatar Benjamin Peterson

normalize exceptions passed to the __exit__ method #7853

In Python 2.x, exceptions in finally blocks are not normalized.  Since with
statements are implemented using finally blocks, ceval.c had to be tweaked to
distinguish between with finally blocks and normal ones.

A test for the finalization of generators containing with statements was also
added.
parent 4a7ff1d8
......@@ -1700,6 +1700,17 @@ And finalization:
>>> del g
exiting
>>> class context(object):
... def __enter__(self): pass
... def __exit__(self, *args): print 'exiting'
>>> def f():
... with context():
... yield
>>> g = f()
>>> g.next()
>>> del g
exiting
GeneratorExit is not caught by except Exception:
......
......@@ -361,7 +361,6 @@ class ExceptionalTestCase(unittest.TestCase, ContextmanagerAssertionMixin):
self.assertAfterWithManagerInvariantsWithError(cm)
self.assertAfterWithGeneratorInvariantsWithError(self.resource)
@unittest.expectedFailure
def testExceptionNormalized(self):
cm = mock_contextmanager_generator()
def shouldThrow():
......
......@@ -12,6 +12,9 @@ What's New in Python 2.7 alpha 3?
Core and Builtins
-----------------
- Issue #7853: Normalize exceptions before they are passed to a context managers
__exit__ method.
- Issue #7385: Fix a crash in `MemoryView_FromObject` when
`PyObject_GetBuffer` fails. Patch by Florent Xicluna.
......
......@@ -2555,9 +2555,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
Py_DECREF(u);
if (!x)
break;
/* Setup the finally block before pushing the result
of __enter__ on the stack. */
PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
/* Setup a finally block (SETUP_WITH as a block is
equivalent to SETUP_FINALLY except it normalizes
the exception) before pushing the result of
__enter__ on the stack. */
PyFrame_BlockSetup(f, SETUP_WITH, INSTR_OFFSET() + oparg,
STACK_LEVEL());
PUSH(x);
......@@ -2898,7 +2900,8 @@ fast_block_end:
}
if (b->b_type == SETUP_FINALLY ||
(b->b_type == SETUP_EXCEPT &&
why == WHY_EXCEPTION)) {
why == WHY_EXCEPTION) ||
b->b_type == SETUP_WITH) {
if (why == WHY_EXCEPTION) {
PyObject *exc, *val, *tb;
PyErr_Fetch(&exc, &val, &tb);
......@@ -2911,7 +2914,8 @@ fast_block_end:
so a program can emulate the
Python main loop. Don't do
this for 'finally'. */
if (b->b_type == SETUP_EXCEPT) {
if (b->b_type == SETUP_EXCEPT ||
b->b_type == SETUP_WITH) {
PyErr_NormalizeException(
&exc, &val, &tb);
set_exc_info(tstate,
......
......@@ -2927,6 +2927,9 @@ compiler_with(struct compiler *c, stmt_ty s)
/* SETUP_WITH pushes a finally block. */
compiler_use_next_block(c, block);
/* Note that the block is actually called SETUP_WITH in ceval.c, but
functions the same as SETUP_FINALLY except that exceptions are
normalized. */
if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment