mirror of
https://github.com/python/cpython.git
synced 2025-09-20 23:50:22 +00:00
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
11 changed files with 61 additions and 106 deletions
|
@ -779,7 +779,7 @@ class StackDepthTracker:
|
||||||
'SETUP_EXCEPT': 3,
|
'SETUP_EXCEPT': 3,
|
||||||
'SETUP_FINALLY': 3,
|
'SETUP_FINALLY': 3,
|
||||||
'FOR_ITER': 1,
|
'FOR_ITER': 1,
|
||||||
'WITH_CLEANUP': 3,
|
'WITH_CLEANUP': -1,
|
||||||
}
|
}
|
||||||
# use pattern match
|
# use pattern match
|
||||||
patterns = [
|
patterns = [
|
||||||
|
|
|
@ -858,8 +858,6 @@ class CodeGenerator:
|
||||||
self.nextBlock(final)
|
self.nextBlock(final)
|
||||||
self.setups.push((END_FINALLY, final))
|
self.setups.push((END_FINALLY, final))
|
||||||
self.emit('WITH_CLEANUP')
|
self.emit('WITH_CLEANUP')
|
||||||
self.emit('CALL_FUNCTION', 3)
|
|
||||||
self.emit('POP_TOP')
|
|
||||||
self.emit('END_FINALLY')
|
self.emit('END_FINALLY')
|
||||||
self.setups.pop()
|
self.setups.pop()
|
||||||
self.__with_count -= 1
|
self.__with_count -= 1
|
||||||
|
|
|
@ -30,8 +30,9 @@ class GeneratorContextManager(object):
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self.gen.throw(type, value, traceback)
|
self.gen.throw(type, value, traceback)
|
||||||
|
return True
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
pass
|
return True
|
||||||
|
|
||||||
|
|
||||||
def contextmanager(func):
|
def contextmanager(func):
|
||||||
|
@ -91,6 +92,7 @@ def nested(*contexts):
|
||||||
"""
|
"""
|
||||||
exits = []
|
exits = []
|
||||||
vars = []
|
vars = []
|
||||||
|
exc = (None, None, None)
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
|
@ -102,17 +104,14 @@ def nested(*contexts):
|
||||||
yield vars
|
yield vars
|
||||||
except:
|
except:
|
||||||
exc = sys.exc_info()
|
exc = sys.exc_info()
|
||||||
else:
|
|
||||||
exc = (None, None, None)
|
|
||||||
finally:
|
finally:
|
||||||
while exits:
|
while exits:
|
||||||
exit = exits.pop()
|
exit = exits.pop()
|
||||||
try:
|
try:
|
||||||
exit(*exc)
|
if exit(*exc):
|
||||||
|
exc = (None, None, None)
|
||||||
except:
|
except:
|
||||||
exc = sys.exc_info()
|
exc = sys.exc_info()
|
||||||
else:
|
|
||||||
exc = (None, None, None)
|
|
||||||
if exc != (None, None, None):
|
if exc != (None, None, None):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
|
@ -2196,8 +2196,6 @@ class ContextManager(object):
|
||||||
return self.new_context
|
return self.new_context
|
||||||
def __exit__(self, t, v, tb):
|
def __exit__(self, t, v, tb):
|
||||||
setcontext(self.saved_context)
|
setcontext(self.saved_context)
|
||||||
if t is not None:
|
|
||||||
raise t, v, tb
|
|
||||||
|
|
||||||
class Context(object):
|
class Context(object):
|
||||||
"""Contains the context for a Decimal instance.
|
"""Contains the context for a Decimal instance.
|
||||||
|
|
|
@ -78,8 +78,8 @@ class Nested(object):
|
||||||
vars.append(mgr.__enter__())
|
vars.append(mgr.__enter__())
|
||||||
self.entered.appendleft(mgr)
|
self.entered.appendleft(mgr)
|
||||||
except:
|
except:
|
||||||
self.__exit__(*sys.exc_info())
|
if not self.__exit__(*sys.exc_info()):
|
||||||
raise
|
raise
|
||||||
return vars
|
return vars
|
||||||
|
|
||||||
def __exit__(self, *exc_info):
|
def __exit__(self, *exc_info):
|
||||||
|
@ -89,7 +89,8 @@ class Nested(object):
|
||||||
ex = exc_info
|
ex = exc_info
|
||||||
for mgr in self.entered:
|
for mgr in self.entered:
|
||||||
try:
|
try:
|
||||||
mgr.__exit__(*ex)
|
if mgr.__exit__(*ex):
|
||||||
|
ex = (None, None, None)
|
||||||
except:
|
except:
|
||||||
ex = sys.exc_info()
|
ex = sys.exc_info()
|
||||||
self.entered = None
|
self.entered = None
|
||||||
|
@ -574,9 +575,7 @@ class AssignmentTargetTestCase(unittest.TestCase):
|
||||||
class C:
|
class C:
|
||||||
def __context__(self): return self
|
def __context__(self): return self
|
||||||
def __enter__(self): return 1, 2, 3
|
def __enter__(self): return 1, 2, 3
|
||||||
def __exit__(self, t, v, tb):
|
def __exit__(self, t, v, tb): pass
|
||||||
if t is not None:
|
|
||||||
raise t, v, tb
|
|
||||||
targets = {1: [0, 1, 2]}
|
targets = {1: [0, 1, 2]}
|
||||||
with C() as (targets[1][0], targets[1][1], targets[1][2]):
|
with C() as (targets[1][0], targets[1][1], targets[1][2]):
|
||||||
self.assertEqual(targets, {1: [1, 2, 3]})
|
self.assertEqual(targets, {1: [1, 2, 3]})
|
||||||
|
@ -594,17 +593,30 @@ class AssignmentTargetTestCase(unittest.TestCase):
|
||||||
|
|
||||||
class ExitSwallowsExceptionTestCase(unittest.TestCase):
|
class ExitSwallowsExceptionTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def testExitSwallowsException(self):
|
def testExitTrueSwallowsException(self):
|
||||||
class AfricanOrEuropean:
|
class AfricanSwallow:
|
||||||
def __context__(self): return self
|
def __context__(self): return self
|
||||||
def __enter__(self): pass
|
def __enter__(self): pass
|
||||||
def __exit__(self, t, v, tb): pass
|
def __exit__(self, t, v, tb): return True
|
||||||
try:
|
try:
|
||||||
with AfricanOrEuropean():
|
with AfricanSwallow():
|
||||||
1/0
|
1/0
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
self.fail("ZeroDivisionError should have been swallowed")
|
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():
|
def test_main():
|
||||||
run_unittest(FailureTestCase, NonexceptionalTestCase,
|
run_unittest(FailureTestCase, NonexceptionalTestCase,
|
||||||
|
|
|
@ -128,8 +128,6 @@ class _RLock(_Verbose):
|
||||||
|
|
||||||
def __exit__(self, t, v, tb):
|
def __exit__(self, t, v, tb):
|
||||||
self.release()
|
self.release()
|
||||||
if t is not None:
|
|
||||||
raise t, v, tb
|
|
||||||
|
|
||||||
# Internal methods used by condition variables
|
# Internal methods used by condition variables
|
||||||
|
|
||||||
|
@ -190,8 +188,6 @@ class _Condition(_Verbose):
|
||||||
|
|
||||||
def __exit__(self, t, v, tb):
|
def __exit__(self, t, v, tb):
|
||||||
self.release()
|
self.release()
|
||||||
if t is not None:
|
|
||||||
raise t, v, tb
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
|
return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
|
||||||
|
@ -321,8 +317,6 @@ class _Semaphore(_Verbose):
|
||||||
|
|
||||||
def __exit__(self, t, v, tb):
|
def __exit__(self, t, v, tb):
|
||||||
self.release()
|
self.release()
|
||||||
if t is not None:
|
|
||||||
raise t, v, tb
|
|
||||||
|
|
||||||
|
|
||||||
def BoundedSemaphore(*args, **kwargs):
|
def BoundedSemaphore(*args, **kwargs):
|
||||||
|
|
|
@ -68,7 +68,7 @@ lock_PyThread_acquire_lock(lockobject *self, PyObject *args)
|
||||||
|
|
||||||
PyDoc_STRVAR(acquire_doc,
|
PyDoc_STRVAR(acquire_doc,
|
||||||
"acquire([wait]) -> None or bool\n\
|
"acquire([wait]) -> None or bool\n\
|
||||||
(PyThread_acquire_lock() is an obsolete synonym)\n\
|
(acquire_lock() is an obsolete synonym)\n\
|
||||||
\n\
|
\n\
|
||||||
Lock the lock. Without argument, this blocks if the lock is already\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\
|
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,
|
PyDoc_STRVAR(release_doc,
|
||||||
"release()\n\
|
"release()\n\
|
||||||
(PyThread_release_lock() is an obsolete synonym)\n\
|
(release_lock() is an obsolete synonym)\n\
|
||||||
\n\
|
\n\
|
||||||
Release the lock, allowing another thread that is blocked waiting for\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\
|
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;
|
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[] = {
|
static PyMethodDef lock_methods[] = {
|
||||||
{"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
|
{"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
|
||||||
METH_VARARGS, acquire_doc},
|
METH_VARARGS, acquire_doc},
|
||||||
|
@ -163,8 +140,8 @@ static PyMethodDef lock_methods[] = {
|
||||||
METH_NOARGS, PyDoc_STR("__context__() -> self.")},
|
METH_NOARGS, PyDoc_STR("__context__() -> self.")},
|
||||||
{"__enter__", (PyCFunction)lock_PyThread_acquire_lock,
|
{"__enter__", (PyCFunction)lock_PyThread_acquire_lock,
|
||||||
METH_VARARGS, acquire_doc},
|
METH_VARARGS, acquire_doc},
|
||||||
{"__exit__", (PyCFunction)lock_exit,
|
{"__exit__", (PyCFunction)lock_PyThread_release_lock,
|
||||||
METH_VARARGS, lock_exit_doc},
|
METH_VARARGS, release_doc},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1617,24 +1617,6 @@ file_self(PyFileObject *f)
|
||||||
return (PyObject *)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,
|
PyDoc_STRVAR(readline_doc,
|
||||||
"readline([size]) -> next line from the file, as a string.\n"
|
"readline([size]) -> next line from the file, as a string.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -1725,13 +1707,6 @@ PyDoc_STRVAR(context_doc,
|
||||||
PyDoc_STRVAR(enter_doc,
|
PyDoc_STRVAR(enter_doc,
|
||||||
"__enter__() -> self.");
|
"__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[] = {
|
static PyMethodDef file_methods[] = {
|
||||||
{"readline", (PyCFunction)file_readline, METH_VARARGS, readline_doc},
|
{"readline", (PyCFunction)file_readline, METH_VARARGS, readline_doc},
|
||||||
{"read", (PyCFunction)file_read, METH_VARARGS, read_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},
|
{"isatty", (PyCFunction)file_isatty, METH_NOARGS, isatty_doc},
|
||||||
{"__context__", (PyCFunction)file_self, METH_NOARGS, context_doc},
|
{"__context__", (PyCFunction)file_self, METH_NOARGS, context_doc},
|
||||||
{"__enter__", (PyCFunction)file_self, METH_NOARGS, enter_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 */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2189,48 +2189,51 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
|
||||||
Below that are 1-3 values indicating how/why
|
Below that are 1-3 values indicating how/why
|
||||||
we entered the finally clause:
|
we entered the finally clause:
|
||||||
- SECOND = None
|
- SECOND = None
|
||||||
- (SECOND, THIRD) = (WHY_RETURN or WHY_CONTINUE), retval
|
- (SECOND, THIRD) = (WHY_{RETURN,CONTINUE}), retval
|
||||||
- SECOND = WHY_*; no retval below it
|
- SECOND = WHY_*; no retval below it
|
||||||
- (SECOND, THIRD, FOURTH) = exc_info()
|
- (SECOND, THIRD, FOURTH) = exc_info()
|
||||||
In the last case, we must call
|
In the last case, we must call
|
||||||
TOP(SECOND, THIRD, FOURTH)
|
TOP(SECOND, THIRD, FOURTH)
|
||||||
otherwise we must call
|
otherwise we must call
|
||||||
TOP(None, None, None)
|
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,
|
In addition, if the stack represents an exception,
|
||||||
we "zap" this information; __exit__() should
|
*and* the function call returns a 'true' value, we
|
||||||
re-raise the exception if it wants to, and if
|
"zap" this information, to prevent END_FINALLY from
|
||||||
__exit__() returns normally, END_FINALLY should
|
re-raising the exception. (But non-local gotos
|
||||||
*not* re-raise the exception. (But non-local
|
should still be resumed.)
|
||||||
gotos should still be resumed.)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
x = TOP();
|
x = TOP();
|
||||||
u = SECOND();
|
u = SECOND();
|
||||||
if (PyInt_Check(u) || u == Py_None) {
|
if (PyInt_Check(u) || u == Py_None) {
|
||||||
u = v = w = Py_None;
|
u = v = w = Py_None;
|
||||||
Py_INCREF(u);
|
|
||||||
Py_INCREF(v);
|
|
||||||
Py_INCREF(w);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
v = THIRD();
|
v = THIRD();
|
||||||
w = FOURTH();
|
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);
|
/* XXX Not the fastest way to call it... */
|
||||||
SET_THIRD(u);
|
x = PyObject_CallFunctionObjArgs(x, u, v, w, NULL);
|
||||||
SET_SECOND(v);
|
if (x == NULL)
|
||||||
SET_TOP(w);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1382,7 +1382,7 @@ opcode_stack_effect(int opcode, int oparg)
|
||||||
case BREAK_LOOP:
|
case BREAK_LOOP:
|
||||||
return 0;
|
return 0;
|
||||||
case WITH_CLEANUP:
|
case WITH_CLEANUP:
|
||||||
return 3;
|
return -1; /* XXX Sometimes more */
|
||||||
case LOAD_LOCALS:
|
case LOAD_LOCALS:
|
||||||
return 1;
|
return 1;
|
||||||
case RETURN_VALUE:
|
case RETURN_VALUE:
|
||||||
|
@ -3472,8 +3472,6 @@ compiler_with(struct compiler *c, stmt_ty s)
|
||||||
!compiler_nameop(c, tmpexit, Del))
|
!compiler_nameop(c, tmpexit, Del))
|
||||||
return 0;
|
return 0;
|
||||||
ADDOP(c, WITH_CLEANUP);
|
ADDOP(c, WITH_CLEANUP);
|
||||||
ADDOP_I(c, CALL_FUNCTION, 3);
|
|
||||||
ADDOP(c, POP_TOP);
|
|
||||||
|
|
||||||
/* Finally block ends. */
|
/* Finally block ends. */
|
||||||
ADDOP(c, END_FINALLY);
|
ADDOP(c, END_FINALLY);
|
||||||
|
|
|
@ -55,6 +55,7 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
|
||||||
Python 2.5a0: 62071
|
Python 2.5a0: 62071
|
||||||
Python 2.5a0: 62081 (ast-branch)
|
Python 2.5a0: 62081 (ast-branch)
|
||||||
Python 2.5a0: 62091 (with)
|
Python 2.5a0: 62091 (with)
|
||||||
|
Python 2.5a0: 62092 (changed WITH_CLEANUP opcode)
|
||||||
.
|
.
|
||||||
*/
|
*/
|
||||||
#define MAGIC (62092 | ((long)'\r'<<16) | ((long)'\n'<<24))
|
#define MAGIC (62092 | ((long)'\r'<<16) | ((long)'\n'<<24))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue