Commit f6694361 authored by Guido van Rossum's avatar Guido van Rossum

Um, I thought I'd already checked this in.

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.
parent 692cdbc5
......@@ -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 = [
......
......@@ -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
......
......@@ -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
......
......@@ -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.
......
......@@ -78,7 +78,7 @@ class Nested(object):
vars.append(mgr.__enter__())
self.entered.appendleft(mgr)
except:
self.__exit__(*sys.exc_info())
if not self.__exit__(*sys.exc_info()):
raise
return vars
......@@ -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,
......
......@@ -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):
......
......@@ -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 */
};
......
......@@ -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 */
};
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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))
......
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