[Python-checkins] r77983 - in python/trunk: Lib/test/test_generators.py Lib/test/test_with.py Misc/NEWS Python/ceval.c Python/compile.c

benjamin.peterson python-checkins at python.org
Fri Feb 5 03:12:14 CET 2010


Author: benjamin.peterson
Date: Fri Feb  5 03:12:14 2010
New Revision: 77983

Log:
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.


Modified:
   python/trunk/Lib/test/test_generators.py
   python/trunk/Lib/test/test_with.py
   python/trunk/Misc/NEWS
   python/trunk/Python/ceval.c
   python/trunk/Python/compile.c

Modified: python/trunk/Lib/test/test_generators.py
==============================================================================
--- python/trunk/Lib/test/test_generators.py	(original)
+++ python/trunk/Lib/test/test_generators.py	Fri Feb  5 03:12:14 2010
@@ -1700,6 +1700,17 @@
 >>> 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:
 

Modified: python/trunk/Lib/test/test_with.py
==============================================================================
--- python/trunk/Lib/test/test_with.py	(original)
+++ python/trunk/Lib/test/test_with.py	Fri Feb  5 03:12:14 2010
@@ -361,7 +361,6 @@
         self.assertAfterWithManagerInvariantsWithError(cm)
         self.assertAfterWithGeneratorInvariantsWithError(self.resource)
 
-    @unittest.expectedFailure
     def testExceptionNormalized(self):
         cm = mock_contextmanager_generator()
         def shouldThrow():

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Fri Feb  5 03:12:14 2010
@@ -12,6 +12,9 @@
 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.
 

Modified: python/trunk/Python/ceval.c
==============================================================================
--- python/trunk/Python/ceval.c	(original)
+++ python/trunk/Python/ceval.c	Fri Feb  5 03:12:14 2010
@@ -2555,9 +2555,11 @@
 			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 @@
 			}
 			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 @@
 					   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,

Modified: python/trunk/Python/compile.c
==============================================================================
--- python/trunk/Python/compile.c	(original)
+++ python/trunk/Python/compile.c	Fri Feb  5 03:12:14 2010
@@ -2927,6 +2927,9 @@
 
     /* 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;
     }


More information about the Python-checkins mailing list