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.
This commit is contained in:
parent
692cdbc5d6
commit
f669436189
|
@ -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);
|
||||
Py_INCREF(Py_None);
|
||||
SET_SECOND(Py_None);
|
||||
}
|
||||
STACKADJ(3);
|
||||
SET_THIRD(u);
|
||||
SET_SECOND(v);
|
||||
SET_TOP(w);
|
||||
/* 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_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);
|
||||
}
|
||||
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))
|
||||
|
|
Loading…
Reference in New Issue