Skip to content

Commit f669436

Browse files
committed
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.
1 parent 692cdbc commit f669436

11 files changed

Lines changed: 60 additions & 105 deletions

File tree

Lib/compiler/pyassem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ def findDepth(self, insts, debug=0):
779779
'SETUP_EXCEPT': 3,
780780
'SETUP_FINALLY': 3,
781781
'FOR_ITER': 1,
782-
'WITH_CLEANUP': 3,
782+
'WITH_CLEANUP': -1,
783783
}
784784
# use pattern match
785785
patterns = [

Lib/compiler/pycodegen.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -858,8 +858,6 @@ def visitWith(self, node):
858858
self.nextBlock(final)
859859
self.setups.push((END_FINALLY, final))
860860
self.emit('WITH_CLEANUP')
861-
self.emit('CALL_FUNCTION', 3)
862-
self.emit('POP_TOP')
863861
self.emit('END_FINALLY')
864862
self.setups.pop()
865863
self.__with_count -= 1

Lib/contextlib.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ def __exit__(self, type, value, traceback):
3030
else:
3131
try:
3232
self.gen.throw(type, value, traceback)
33+
return True
3334
except StopIteration:
34-
pass
35+
return True
3536

3637

3738
def contextmanager(func):
@@ -91,6 +92,7 @@ def nested(*contexts):
9192
"""
9293
exits = []
9394
vars = []
95+
exc = (None, None, None)
9496
try:
9597
try:
9698
for context in contexts:
@@ -102,17 +104,14 @@ def nested(*contexts):
102104
yield vars
103105
except:
104106
exc = sys.exc_info()
105-
else:
106-
exc = (None, None, None)
107107
finally:
108108
while exits:
109109
exit = exits.pop()
110110
try:
111-
exit(*exc)
111+
if exit(*exc):
112+
exc = (None, None, None)
112113
except:
113114
exc = sys.exc_info()
114-
else:
115-
exc = (None, None, None)
116115
if exc != (None, None, None):
117116
raise
118117

Lib/decimal.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,8 +2196,6 @@ def __enter__(self):
21962196
return self.new_context
21972197
def __exit__(self, t, v, tb):
21982198
setcontext(self.saved_context)
2199-
if t is not None:
2200-
raise t, v, tb
22012199

22022200
class Context(object):
22032201
"""Contains the context for a Decimal instance.

Lib/test/test_with.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ def __enter__(self):
7878
vars.append(mgr.__enter__())
7979
self.entered.appendleft(mgr)
8080
except:
81-
self.__exit__(*sys.exc_info())
82-
raise
81+
if not self.__exit__(*sys.exc_info()):
82+
raise
8383
return vars
8484

8585
def __exit__(self, *exc_info):
@@ -89,7 +89,8 @@ def __exit__(self, *exc_info):
8989
ex = exc_info
9090
for mgr in self.entered:
9191
try:
92-
mgr.__exit__(*ex)
92+
if mgr.__exit__(*ex):
93+
ex = (None, None, None)
9394
except:
9495
ex = sys.exc_info()
9596
self.entered = None
@@ -574,9 +575,7 @@ def testMultipleComplexTargets(self):
574575
class C:
575576
def __context__(self): return self
576577
def __enter__(self): return 1, 2, 3
577-
def __exit__(self, t, v, tb):
578-
if t is not None:
579-
raise t, v, tb
578+
def __exit__(self, t, v, tb): pass
580579
targets = {1: [0, 1, 2]}
581580
with C() as (targets[1][0], targets[1][1], targets[1][2]):
582581
self.assertEqual(targets, {1: [1, 2, 3]})
@@ -594,17 +593,30 @@ class B: pass
594593

595594
class ExitSwallowsExceptionTestCase(unittest.TestCase):
596595

597-
def testExitSwallowsException(self):
598-
class AfricanOrEuropean:
596+
def testExitTrueSwallowsException(self):
597+
class AfricanSwallow:
599598
def __context__(self): return self
600599
def __enter__(self): pass
601-
def __exit__(self, t, v, tb): pass
600+
def __exit__(self, t, v, tb): return True
602601
try:
603-
with AfricanOrEuropean():
602+
with AfricanSwallow():
604603
1/0
605604
except ZeroDivisionError:
606605
self.fail("ZeroDivisionError should have been swallowed")
607606

607+
def testExitFalseDoesntSwallowException(self):
608+
class EuropeanSwallow:
609+
def __context__(self): return self
610+
def __enter__(self): pass
611+
def __exit__(self, t, v, tb): return False
612+
try:
613+
with EuropeanSwallow():
614+
1/0
615+
except ZeroDivisionError:
616+
pass
617+
else:
618+
self.fail("ZeroDivisionError should have been raised")
619+
608620

609621
def test_main():
610622
run_unittest(FailureTestCase, NonexceptionalTestCase,

Lib/threading.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,6 @@ def release(self):
128128

129129
def __exit__(self, t, v, tb):
130130
self.release()
131-
if t is not None:
132-
raise t, v, tb
133131

134132
# Internal methods used by condition variables
135133

@@ -190,8 +188,6 @@ def __context__(self):
190188

191189
def __exit__(self, t, v, tb):
192190
self.release()
193-
if t is not None:
194-
raise t, v, tb
195191

196192
def __repr__(self):
197193
return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
@@ -321,8 +317,6 @@ def release(self):
321317

322318
def __exit__(self, t, v, tb):
323319
self.release()
324-
if t is not None:
325-
raise t, v, tb
326320

327321

328322
def BoundedSemaphore(*args, **kwargs):

Modules/threadmodule.c

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ lock_PyThread_acquire_lock(lockobject *self, PyObject *args)
6868

6969
PyDoc_STRVAR(acquire_doc,
7070
"acquire([wait]) -> None or bool\n\
71-
(PyThread_acquire_lock() is an obsolete synonym)\n\
71+
(acquire_lock() is an obsolete synonym)\n\
7272
\n\
7373
Lock the lock. Without argument, this blocks if the lock is already\n\
7474
locked (even by the same thread), waiting for another thread to release\n\
@@ -94,7 +94,7 @@ lock_PyThread_release_lock(lockobject *self)
9494

9595
PyDoc_STRVAR(release_doc,
9696
"release()\n\
97-
(PyThread_release_lock() is an obsolete synonym)\n\
97+
(release_lock() is an obsolete synonym)\n\
9898
\n\
9999
Release the lock, allowing another thread that is blocked waiting for\n\
100100
the lock to acquire the lock. The lock must be in the locked state,\n\
@@ -123,29 +123,6 @@ lock_context(lockobject *self)
123123
return (PyObject *)self;
124124
}
125125

126-
PyDoc_STRVAR(lock_exit_doc,
127-
"__exit__(type, value, tb)\n\
128-
\n\
129-
Releases the lock; then re-raises the exception if type is not None.");
130-
131-
static PyObject *
132-
lock_exit(lockobject *self, PyObject *args)
133-
{
134-
PyObject *type, *value, *tb, *result;
135-
if (!PyArg_ParseTuple(args, "OOO:__exit__", &type, &value, &tb))
136-
return NULL;
137-
result = lock_PyThread_release_lock(self);
138-
if (result != NULL && type != Py_None) {
139-
Py_DECREF(result);
140-
result = NULL;
141-
Py_INCREF(type);
142-
Py_INCREF(value);
143-
Py_INCREF(tb);
144-
PyErr_Restore(type, value, tb);
145-
}
146-
return result;
147-
}
148-
149126
static PyMethodDef lock_methods[] = {
150127
{"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock,
151128
METH_VARARGS, acquire_doc},
@@ -163,8 +140,8 @@ static PyMethodDef lock_methods[] = {
163140
METH_NOARGS, PyDoc_STR("__context__() -> self.")},
164141
{"__enter__", (PyCFunction)lock_PyThread_acquire_lock,
165142
METH_VARARGS, acquire_doc},
166-
{"__exit__", (PyCFunction)lock_exit,
167-
METH_VARARGS, lock_exit_doc},
143+
{"__exit__", (PyCFunction)lock_PyThread_release_lock,
144+
METH_VARARGS, release_doc},
168145
{NULL, NULL} /* sentinel */
169146
};
170147

Objects/fileobject.c

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,24 +1617,6 @@ file_self(PyFileObject *f)
16171617
return (PyObject *)f;
16181618
}
16191619

1620-
static PyObject *
1621-
file_exit(PyFileObject *f, PyObject *args)
1622-
{
1623-
PyObject *type, *value, *tb, *result;
1624-
if (!PyArg_ParseTuple(args, "OOO:__exit__", &type, &value, &tb))
1625-
return NULL;
1626-
result = file_close(f);
1627-
if (result != NULL && type != Py_None) {
1628-
Py_DECREF(result);
1629-
result = NULL;
1630-
Py_INCREF(type);
1631-
Py_INCREF(value);
1632-
Py_INCREF(tb);
1633-
PyErr_Restore(type, value, tb);
1634-
}
1635-
return result;
1636-
}
1637-
16381620
PyDoc_STRVAR(readline_doc,
16391621
"readline([size]) -> next line from the file, as a string.\n"
16401622
"\n"
@@ -1725,13 +1707,6 @@ PyDoc_STRVAR(context_doc,
17251707
PyDoc_STRVAR(enter_doc,
17261708
"__enter__() -> self.");
17271709

1728-
PyDoc_STRVAR(exit_doc,
1729-
"__exit__(type, value, traceback).\n\
1730-
\n\
1731-
Closes the file; then re-raises the exception if type is not None.\n\
1732-
If no exception is re-raised, the return value is the same as for close().\n\
1733-
");
1734-
17351710
static PyMethodDef file_methods[] = {
17361711
{"readline", (PyCFunction)file_readline, METH_VARARGS, readline_doc},
17371712
{"read", (PyCFunction)file_read, METH_VARARGS, read_doc},
@@ -1751,7 +1726,7 @@ static PyMethodDef file_methods[] = {
17511726
{"isatty", (PyCFunction)file_isatty, METH_NOARGS, isatty_doc},
17521727
{"__context__", (PyCFunction)file_self, METH_NOARGS, context_doc},
17531728
{"__enter__", (PyCFunction)file_self, METH_NOARGS, enter_doc},
1754-
{"__exit__", (PyCFunction)file_exit, METH_VARARGS, exit_doc},
1729+
{"__exit__", (PyCFunction)file_close, METH_VARARGS, close_doc},
17551730
{NULL, NULL} /* sentinel */
17561731
};
17571732

Python/ceval.c

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,48 +2189,51 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw)
21892189
Below that are 1-3 values indicating how/why
21902190
we entered the finally clause:
21912191
- SECOND = None
2192-
- (SECOND, THIRD) = (WHY_RETURN or WHY_CONTINUE), retval
2192+
- (SECOND, THIRD) = (WHY_{RETURN,CONTINUE}), retval
21932193
- SECOND = WHY_*; no retval below it
21942194
- (SECOND, THIRD, FOURTH) = exc_info()
21952195
In the last case, we must call
21962196
TOP(SECOND, THIRD, FOURTH)
21972197
otherwise we must call
21982198
TOP(None, None, None)
2199-
but we must preserve the stack entries below TOP.
2200-
The code here just sets the stack up for the call;
2201-
separate CALL_FUNCTION(3) and POP_TOP opcodes are
2202-
emitted by the compiler.
22032199
22042200
In addition, if the stack represents an exception,
2205-
we "zap" this information; __exit__() should
2206-
re-raise the exception if it wants to, and if
2207-
__exit__() returns normally, END_FINALLY should
2208-
*not* re-raise the exception. (But non-local
2209-
gotos should still be resumed.)
2201+
*and* the function call returns a 'true' value, we
2202+
"zap" this information, to prevent END_FINALLY from
2203+
re-raising the exception. (But non-local gotos
2204+
should still be resumed.)
22102205
*/
22112206

22122207
x = TOP();
22132208
u = SECOND();
22142209
if (PyInt_Check(u) || u == Py_None) {
22152210
u = v = w = Py_None;
2216-
Py_INCREF(u);
2217-
Py_INCREF(v);
2218-
Py_INCREF(w);
22192211
}
22202212
else {
22212213
v = THIRD();
22222214
w = FOURTH();
2223-
/* Zap the exception from the stack,
2224-
to fool END_FINALLY. */
2225-
STACKADJ(-2);
2226-
SET_TOP(x);
2215+
}
2216+
/* XXX Not the fastest way to call it... */
2217+
x = PyObject_CallFunctionObjArgs(x, u, v, w, NULL);
2218+
if (x == NULL)
2219+
break; /* Go to error exit */
2220+
if (u != Py_None && PyObject_IsTrue(x)) {
2221+
/* There was an exception and a true return */
2222+
Py_DECREF(x);
2223+
x = TOP(); /* Again */
2224+
STACKADJ(-3);
22272225
Py_INCREF(Py_None);
2228-
SET_SECOND(Py_None);
2226+
SET_TOP(Py_None);
2227+
Py_DECREF(x);
2228+
Py_DECREF(u);
2229+
Py_DECREF(v);
2230+
Py_DECREF(w);
2231+
} else {
2232+
/* Let END_FINALLY do its thing */
2233+
Py_DECREF(x);
2234+
x = POP();
2235+
Py_DECREF(x);
22292236
}
2230-
STACKADJ(3);
2231-
SET_THIRD(u);
2232-
SET_SECOND(v);
2233-
SET_TOP(w);
22342237
break;
22352238
}
22362239

Python/compile.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,7 @@ opcode_stack_effect(int opcode, int oparg)
13821382
case BREAK_LOOP:
13831383
return 0;
13841384
case WITH_CLEANUP:
1385-
return 3;
1385+
return -1; /* XXX Sometimes more */
13861386
case LOAD_LOCALS:
13871387
return 1;
13881388
case RETURN_VALUE:
@@ -3472,8 +3472,6 @@ compiler_with(struct compiler *c, stmt_ty s)
34723472
!compiler_nameop(c, tmpexit, Del))
34733473
return 0;
34743474
ADDOP(c, WITH_CLEANUP);
3475-
ADDOP_I(c, CALL_FUNCTION, 3);
3476-
ADDOP(c, POP_TOP);
34773475

34783476
/* Finally block ends. */
34793477
ADDOP(c, END_FINALLY);

0 commit comments

Comments
 (0)