summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_generators.py11
-rw-r--r--Lib/test/test_with.py1
-rw-r--r--Misc/NEWS3
-rw-r--r--Python/ceval.c14
-rw-r--r--Python/compile.c3
5 files changed, 26 insertions, 6 deletions
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index ad7e17c..19bfe07 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -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:
diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py
index 4b947d8..a1ec80b 100644
--- a/Lib/test/test_with.py
+++ b/Lib/test/test_with.py
@@ -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():
diff --git a/Misc/NEWS b/Misc/NEWS
index 6bb355b..9225c6f 100644
--- 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.
diff --git a/Python/ceval.c b/Python/ceval.c
index d501a4e..0b8a377 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -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,
diff --git a/Python/compile.c b/Python/compile.c
index 1e27539..4ea2ee9 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -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;
}