]> granicus.if.org Git - python/commitdiff
Um, I thought I'd already checked this in.
authorGuido van Rossum <guido@python.org>
Fri, 10 Mar 2006 02:28:35 +0000 (02:28 +0000)
committerGuido van Rossum <guido@python.org>
Fri, 10 Mar 2006 02:28:35 +0000 (02:28 +0000)
Anyway, this is the changes to the with-statement
so that __exit__ must return a true value in order
for a pending exception to be ignored.
The PEP (343) is already updated.

Lib/compiler/pyassem.py
Lib/compiler/pycodegen.py
Lib/contextlib.py
Lib/decimal.py
Lib/test/test_with.py
Lib/threading.py
Modules/threadmodule.c
Objects/fileobject.c
Python/ceval.c
Python/compile.c
Python/import.c

index b59661f4e15cceea8821ffb02adac91123c14666..82ff3966480a466a694e4d5ea0b8141bcc40856c 100644 (file)
@@ -779,7 +779,7 @@ class StackDepthTracker:
         'SETUP_EXCEPT': 3,
         'SETUP_FINALLY': 3,
         'FOR_ITER': 1,
-        'WITH_CLEANUP': 3,
+        'WITH_CLEANUP': -1,
         }
     # use pattern match
     patterns = [
index a1236de6340b427b24dcba3fdb4b60a243cf2d89..2b3a24f744157526ae81017eccc85d8bac83a6a9 100644 (file)
@@ -858,8 +858,6 @@ class CodeGenerator:
         self.nextBlock(final)
         self.setups.push((END_FINALLY, final))
         self.emit('WITH_CLEANUP')
-        self.emit('CALL_FUNCTION', 3)
-        self.emit('POP_TOP')
         self.emit('END_FINALLY')
         self.setups.pop()
         self.__with_count -= 1
index 33c302dd96fbcf0ee00ab1a0274d88b1b3224196..0a5d608503ab25fa0b64adf1ea80ac2752fa9f39 100644 (file)
@@ -30,8 +30,9 @@ class GeneratorContextManager(object):
         else:
             try:
                 self.gen.throw(type, value, traceback)
+                return True
             except StopIteration:
-                pass
+                return True
 
 
 def contextmanager(func):
@@ -91,6 +92,7 @@ def nested(*contexts):
     """
     exits = []
     vars = []
+    exc = (None, None, None)
     try:
         try:
             for context in contexts:
@@ -102,17 +104,14 @@ def nested(*contexts):
             yield vars
         except:
             exc = sys.exc_info()
-        else:
-            exc = (None, None, None)
     finally:
         while exits:
             exit = exits.pop()
             try:
-                exit(*exc)
+                if exit(*exc):
+                    exc = (None, None, None)
             except:
                 exc = sys.exc_info()
-            else:
-                exc = (None, None, None)
         if exc != (None, None, None):
             raise
 
index 49f8115f2e4255c91b4d0976e48e736d0be13f55..967f101c61b553ba69cd503c604b7a718edf1dd2 100644 (file)
@@ -2196,8 +2196,6 @@ class ContextManager(object):
         return self.new_context
     def __exit__(self, t, v, tb):
         setcontext(self.saved_context)
-        if t is not None:
-            raise t, v, tb
 
 class Context(object):
     """Contains the context for a Decimal instance.
index 36035e3c00550938a1e4b3b61da1d8c03ad0016a..4854436a642f1e6b5371c452cfc1f54950edf848 100644 (file)
@@ -78,8 +78,8 @@ class Nested(object):
                 vars.append(mgr.__enter__())
                 self.entered.appendleft(mgr)
         except:
-            self.__exit__(*sys.exc_info())
-            raise
+            if not self.__exit__(*sys.exc_info()):
+                raise
         return vars
 
     def __exit__(self, *exc_info):
@@ -89,7 +89,8 @@ class Nested(object):
         ex = exc_info
         for mgr in self.entered:
             try:
-                mgr.__exit__(*ex)
+                if mgr.__exit__(*ex):
+                    ex = (None, None, None)
             except:
                 ex = sys.exc_info()
         self.entered = None
@@ -574,9 +575,7 @@ class AssignmentTargetTestCase(unittest.TestCase):
         class C:
             def __context__(self): return self
             def __enter__(self): return 1, 2, 3
-            def __exit__(self, t, v, tb):
-                if t is not None:
-                    raise t, v, tb
+            def __exit__(self, t, v, tb): pass
         targets = {1: [0, 1, 2]}
         with C() as (targets[1][0], targets[1][1], targets[1][2]):
             self.assertEqual(targets, {1: [1, 2, 3]})
@@ -594,17 +593,30 @@ class AssignmentTargetTestCase(unittest.TestCase):
 
 class ExitSwallowsExceptionTestCase(unittest.TestCase):
 
-    def testExitSwallowsException(self):
-        class AfricanOrEuropean:
+    def testExitTrueSwallowsException(self):
+        class AfricanSwallow:
             def __context__(self): return self
             def __enter__(self): pass
-            def __exit__(self, t, v, tb): pass
+            def __exit__(self, t, v, tb): return True
         try:
-            with AfricanOrEuropean():
+            with AfricanSwallow():
                 1/0
         except ZeroDivisionError:
             self.fail("ZeroDivisionError should have been swallowed")
 
+    def testExitFalseDoesntSwallowException(self):
+        class EuropeanSwallow:
+            def __context__(self): return self
+            def __enter__(self): pass
+            def __exit__(self, t, v, tb): return False
+        try:
+            with EuropeanSwallow():
+                1/0
+        except ZeroDivisionError:
+            pass
+        else:
+            self.fail("ZeroDivisionError should have been raised")
+
 
 def test_main():
     run_unittest(FailureTestCase, NonexceptionalTestCase,
index 5b485d544920110166065112061f7b41daeabfa0..cc1adcece69b46ce98682e1fcb6432f73af25814 100644 (file)
@@ -128,8 +128,6 @@ class _RLock(_Verbose):
 
     def __exit__(self, t, v, tb):
         self.release()
-        if t is not None:
-            raise t, v, tb
 
     # Internal methods used by condition variables
 
@@ -190,8 +188,6 @@ class _Condition(_Verbose):
 
     def __exit__(self, t, v, tb):
         self.release()
-        if t is not None:
-            raise t, v, tb
 
     def __repr__(self):
         return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
@@ -321,8 +317,6 @@ class _Semaphore(_Verbose):
 
     def __exit__(self, t, v, tb):
         self.release()
-        if t is not None:
-            raise t, v, tb
 
 
 def BoundedSemaphore(*args, **kwargs):
index f15cacb50eb66c8a08834f3613e9fa9d3d18e512..9a6c5d80573576723be483679a49ee8f143ecc82 100644 (file)
@@ -68,7 +68,7 @@ lock_PyThread_acquire_lock(lockobject *self, PyObject *args)
 
 PyDoc_STRVAR(acquire_doc,
 "acquire([wait]) -> None or bool\n\
-(PyThread_acquire_lock() is an obsolete synonym)\n\
+(acquire_lock() is an obsolete synonym)\n\
 \n\
 Lock the lock.  Without argument, this blocks if the lock is already\n\
 locked (even by the same thread), waiting for another thread to release\n\
@@ -94,7 +94,7 @@ lock_PyThread_release_lock(lockobject *self)
 
 PyDoc_STRVAR(release_doc,
 "release()\n\
-(PyThread_release_lock() is an obsolete synonym)\n\
+(release_lock() is an obsolete synonym)\n\
 \n\
 Release the lock, allowing another thread that is blocked waiting for\n\
 the lock to acquire the lock.  The lock must be in the locked state,\n\
@@ -123,29 +123,6 @@ lock_context(lockobject *self)
        return (PyObject *)self;
 }
 
-PyDoc_STRVAR(lock_exit_doc,
-"__exit__(type, value, tb)\n\
-\n\
-Releases the lock; then re-raises the exception if type is not None.");
-
-static PyObject *
-lock_exit(lockobject *self, PyObject *args)
-{
-       PyObject *type, *value, *tb, *result;
-       if (!PyArg_ParseTuple(args, "OOO:__exit__", &type, &value, &tb))
-               return NULL;
-       result = lock_PyThread_release_lock(self);
-       if (result != NULL && type != Py_None) {
-               Py_DECREF(result);
-               result = NULL;
-               Py_INCREF(type);
-               Py_INCREF(value);
-               Py_INCREF(tb);
-               PyErr_Restore(type, value, tb);
-       }
-       return result;
-}
-
 static PyMethodDef lock_methods[] = {
        {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, 
         METH_VARARGS, acquire_doc},
@@ -163,8 +140,8 @@ static PyMethodDef lock_methods[] = {
         METH_NOARGS, PyDoc_STR("__context__() -> self.")},
        {"__enter__",    (PyCFunction)lock_PyThread_acquire_lock,
         METH_VARARGS, acquire_doc},
-       {"__exit__",    (PyCFunction)lock_exit,
-        METH_VARARGS, lock_exit_doc},
+       {"__exit__",    (PyCFunction)lock_PyThread_release_lock,
+        METH_VARARGS, release_doc},
        {NULL,           NULL}          /* sentinel */
 };
 
index b39a10f5f4b15ee27d516cbbc1b7f009b00f0784..57a9e9d39bb2da16859d9604e53852662c490308 100644 (file)
@@ -1617,24 +1617,6 @@ file_self(PyFileObject *f)
        return (PyObject *)f;
 }
 
-static PyObject *
-file_exit(PyFileObject *f, PyObject *args)
-{
-       PyObject *type, *value, *tb, *result;
-       if (!PyArg_ParseTuple(args, "OOO:__exit__", &type, &value, &tb))
-               return NULL;
-       result = file_close(f);
-       if (result != NULL && type != Py_None) {
-               Py_DECREF(result);
-               result = NULL;
-               Py_INCREF(type);
-               Py_INCREF(value);
-               Py_INCREF(tb);
-               PyErr_Restore(type, value, tb);
-       }
-       return result;
-}
-
 PyDoc_STRVAR(readline_doc,
 "readline([size]) -> next line from the file, as a string.\n"
 "\n"
@@ -1725,13 +1707,6 @@ PyDoc_STRVAR(context_doc,
 PyDoc_STRVAR(enter_doc,
             "__enter__() -> self.");
 
-PyDoc_STRVAR(exit_doc,
-"__exit__(type, value, traceback).\n\
-\n\
-Closes the file; then re-raises the exception if type is not None.\n\
-If no exception is re-raised, the return value is the same as for close().\n\
-");
-
 static PyMethodDef file_methods[] = {
        {"readline",  (PyCFunction)file_readline, METH_VARARGS, readline_doc},
        {"read",      (PyCFunction)file_read,     METH_VARARGS, read_doc},
@@ -1751,7 +1726,7 @@ static PyMethodDef file_methods[] = {
        {"isatty",    (PyCFunction)file_isatty,   METH_NOARGS,  isatty_doc},
        {"__context__", (PyCFunction)file_self,   METH_NOARGS,  context_doc},
        {"__enter__", (PyCFunction)file_self,     METH_NOARGS,  enter_doc},
-       {"__exit__",  (PyCFunction)file_exit,     METH_VARARGS, exit_doc},
+       {"__exit__",  (PyCFunction)file_close,    METH_VARARGS, close_doc},
        {NULL,        NULL}             /* sentinel */
 };
 
index e7fb875b3c9a563ab33b3c34b93d6bbe4befc972..de2b35b4af51278c7768b0fb814cc58fbd6d75a9 100644 (file)
@@ -2189,48 +2189,51 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
                           Below that are 1-3 values indicating how/why
                           we entered the finally clause:
                           - SECOND = None
-                          - (SECOND, THIRD) = (WHY_RETURN or WHY_CONTINUE), retval
+                          - (SECOND, THIRD) = (WHY_{RETURN,CONTINUE}), retval
                           - SECOND = WHY_*; no retval below it
                           - (SECOND, THIRD, FOURTH) = exc_info()
                           In the last case, we must call
                             TOP(SECOND, THIRD, FOURTH)
                           otherwise we must call
                             TOP(None, None, None)
-                          but we must preserve the stack entries below TOP.
-                          The code here just sets the stack up for the call;
-                          separate CALL_FUNCTION(3) and POP_TOP opcodes are
-                          emitted by the compiler.
 
                           In addition, if the stack represents an exception,
-                          we "zap" this information; __exit__() should
-                          re-raise the exception if it wants to, and if
-                          __exit__() returns normally, END_FINALLY should
-                          *not* re-raise the exception.  (But non-local
-                          gotos should still be resumed.)
+                          *and* the function call returns a 'true' value, we
+                          "zap" this information, to prevent END_FINALLY from
+                          re-raising the exception.  (But non-local gotos
+                          should still be resumed.)
                        */
                        
                        x = TOP();
                        u = SECOND();
                        if (PyInt_Check(u) || u == Py_None) {
                                u = v = w = Py_None;
-                               Py_INCREF(u);
-                               Py_INCREF(v);
-                               Py_INCREF(w);
                        }
                        else {
                                v = THIRD();
                                w = FOURTH();
-                               /* Zap the exception from the stack,
-                                  to fool END_FINALLY. */
-                               STACKADJ(-2);
-                               SET_TOP(x);
+                       }
+                       /* XXX Not the fastest way to call it... */
+                       x = PyObject_CallFunctionObjArgs(x, u, v, w, NULL);
+                       if (x == NULL)
+                               break; /* Go to error exit */
+                       if (u != Py_None && PyObject_IsTrue(x)) {
+                               /* There was an exception and a true return */
+                               Py_DECREF(x);
+                               x = TOP(); /* Again */
+                               STACKADJ(-3);
                                Py_INCREF(Py_None);
-                               SET_SECOND(Py_None);
+                               SET_TOP(Py_None);
+                               Py_DECREF(x);
+                               Py_DECREF(u);
+                               Py_DECREF(v);
+                               Py_DECREF(w);
+                       } else {
+                               /* Let END_FINALLY do its thing */
+                               Py_DECREF(x);
+                               x = POP();
+                               Py_DECREF(x);
                        }
-                       STACKADJ(3);
-                       SET_THIRD(u);
-                       SET_SECOND(v);
-                       SET_TOP(w);
                        break;
                }
 
index c07b6d346da38797fb136c4c1b0c988abd8241d1..e3b3df8f62308982172a1832038320358cc4d0cc 100644 (file)
@@ -1382,7 +1382,7 @@ opcode_stack_effect(int opcode, int oparg)
                case BREAK_LOOP:
                        return 0;
                case WITH_CLEANUP:
-                       return 3;
+                       return -1; /* XXX Sometimes more */
                case LOAD_LOCALS:
                        return 1;
                case RETURN_VALUE:
@@ -3472,8 +3472,6 @@ compiler_with(struct compiler *c, stmt_ty s)
        !compiler_nameop(c, tmpexit, Del))
        return 0;
     ADDOP(c, WITH_CLEANUP);
-    ADDOP_I(c, CALL_FUNCTION, 3);
-    ADDOP(c, POP_TOP);
 
     /* Finally block ends. */
     ADDOP(c, END_FINALLY);
index f214ed5cd2adca9dfe8858b62d2c1f44c3676b99..73051a2060ebdf88eabe7ccdaf774289baf19d3e 100644 (file)
@@ -55,6 +55,7 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
        Python 2.5a0: 62071
        Python 2.5a0: 62081 (ast-branch)
        Python 2.5a0: 62091 (with)
+       Python 2.5a0: 62092 (changed WITH_CLEANUP opcode)
 .
 */
 #define MAGIC (62092 | ((long)'\r'<<16) | ((long)'\n'<<24))