]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
normalize exceptions passed to the __exit__ method #7853
authorBenjamin Peterson <benjamin@python.org>
Fri, 5 Feb 2010 02:12:14 +0000 (02:12 +0000)
committerBenjamin Peterson <benjamin@python.org>
Fri, 5 Feb 2010 02:12:14 +0000 (02:12 +0000)
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.

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

index ad7e17cef50ecae0756b6427b7d460e88e61fbbb..19bfe074c4ecd34dd4ceb22c80d86d482ba3ec87 100644 (file)
@@ -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:
 
index 4b947d82e475eccb84ce366f5d62c00125566f71..a1ec80b3d4db61c2a2780d48a9cf8d59ee98fc1c 100644 (file)
@@ -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():
index 6bb355bdcbfcebe356473e494aa29917d6c6286a..9225c6fc4cee3cd66d671c3eb14fa81ecb87f8e0 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.
 
index d501a4e10a2b4499ef90e50c0b19fa76f0eb00cd..0b8a377ee443e46fe0fe83d9c2f270abc6a1b6a7 100644 (file)
@@ -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,
index 1e275390c988ab009da5b2fb832156c08847089f..4ea2ee9afe3458e7a8d23bbd50574e1f1726ad24 100644 (file)
@@ -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;
     }