Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b4fb6e4

Browse files
committed
Implicit exception chaining via __context__ (PEP 3134).
Patch 3108 by Antooine Pitrou.
1 parent 973124f commit b4fb6e4

4 files changed

Lines changed: 139 additions & 33 deletions

File tree

Lib/test/test_exceptions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,12 @@ def inner_raising_func():
480480
inner_raising_func()
481481
except:
482482
raise KeyError
483-
except KeyError:
483+
except KeyError as e:
484+
# We want to test that the except block above got rid of
485+
# the exception raised in inner_raising_func(), but it
486+
# also ends up in the __context__ of the KeyError, so we
487+
# must clear the latter manually for our test to succeed.
488+
e.__context__ = None
484489
obj = None
485490
obj = wr()
486491
self.failUnless(obj is None, "%s" % obj)

Lib/test/test_raise.py

Lines changed: 96 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -181,32 +181,102 @@ def test_accepts_traceback(self):
181181
self.fail("No exception raised")
182182

183183

184-
# Disabled until context is implemented
185-
# class TestContext(object):
186-
# def test_instance_context_bare_raise(self):
187-
# context = IndexError()
188-
# try:
189-
# try:
190-
# raise context
191-
# except:
192-
# raise OSError()
193-
# except OSError as e:
194-
# self.assertEqual(e.__context__, context)
195-
# else:
196-
# self.fail("No exception raised")
197-
#
198-
# def test_class_context_bare_raise(self):
199-
# context = IndexError
200-
# try:
201-
# try:
202-
# raise context
203-
# except:
204-
# raise OSError()
205-
# except OSError as e:
206-
# self.assertNotEqual(e.__context__, context)
207-
# self.failUnless(isinstance(e.__context__, context))
208-
# else:
209-
# self.fail("No exception raised")
184+
class TestContext(unittest.TestCase):
185+
def test_instance_context_instance_raise(self):
186+
context = IndexError()
187+
try:
188+
try:
189+
raise context
190+
except:
191+
raise OSError()
192+
except OSError as e:
193+
self.assertEqual(e.__context__, context)
194+
else:
195+
self.fail("No exception raised")
196+
197+
def test_class_context_instance_raise(self):
198+
context = IndexError
199+
try:
200+
try:
201+
raise context
202+
except:
203+
raise OSError()
204+
except OSError as e:
205+
self.assertNotEqual(e.__context__, context)
206+
self.failUnless(isinstance(e.__context__, context))
207+
else:
208+
self.fail("No exception raised")
209+
210+
def test_class_context_class_raise(self):
211+
context = IndexError
212+
try:
213+
try:
214+
raise context
215+
except:
216+
raise OSError
217+
except OSError as e:
218+
self.assertNotEqual(e.__context__, context)
219+
self.failUnless(isinstance(e.__context__, context))
220+
else:
221+
self.fail("No exception raised")
222+
223+
def test_c_exception_context(self):
224+
try:
225+
try:
226+
1/0
227+
except:
228+
raise OSError
229+
except OSError as e:
230+
self.failUnless(isinstance(e.__context__, ZeroDivisionError))
231+
else:
232+
self.fail("No exception raised")
233+
234+
def test_c_exception_raise(self):
235+
try:
236+
try:
237+
1/0
238+
except:
239+
xyzzy
240+
except NameError as e:
241+
self.failUnless(isinstance(e.__context__, ZeroDivisionError))
242+
else:
243+
self.fail("No exception raised")
244+
245+
def test_noraise_finally(self):
246+
try:
247+
try:
248+
pass
249+
finally:
250+
raise OSError
251+
except OSError as e:
252+
self.failUnless(e.__context__ is None)
253+
else:
254+
self.fail("No exception raised")
255+
256+
def test_raise_finally(self):
257+
try:
258+
try:
259+
1/0
260+
finally:
261+
raise OSError
262+
except OSError as e:
263+
self.failUnless(isinstance(e.__context__, ZeroDivisionError))
264+
else:
265+
self.fail("No exception raised")
266+
267+
def test_context_manager(self):
268+
class ContextManager:
269+
def __enter__(self):
270+
pass
271+
def __exit__(self, t, v, tb):
272+
xyzzy
273+
try:
274+
with ContextManager():
275+
1/0
276+
except NameError as e:
277+
self.failUnless(isinstance(e.__context__, ZeroDivisionError))
278+
else:
279+
self.fail("No exception raised")
210280

211281

212282
class TestRemovedFunctionality(unittest.TestCase):

Python/ceval.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2817,11 +2817,12 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
28172817
static enum why_code
28182818
do_raise(PyObject *exc, PyObject *cause)
28192819
{
2820-
PyObject *type = NULL, *value = NULL, *tb = NULL;
2820+
PyObject *type = NULL, *value = NULL;
28212821

28222822
if (exc == NULL) {
28232823
/* Reraise */
28242824
PyThreadState *tstate = PyThreadState_GET();
2825+
PyObject *tb;
28252826
type = tstate->exc_type;
28262827
value = tstate->exc_value;
28272828
tb = tstate->exc_traceback;
@@ -2862,7 +2863,6 @@ do_raise(PyObject *exc, PyObject *cause)
28622863
goto raise_error;
28632864
}
28642865

2865-
tb = PyException_GetTraceback(value);
28662866
if (cause) {
28672867
PyObject *fixed_cause;
28682868
if (PyExceptionClass_Check(cause)) {
@@ -2883,13 +2883,15 @@ do_raise(PyObject *exc, PyObject *cause)
28832883
PyException_SetCause(value, fixed_cause);
28842884
}
28852885

2886-
PyErr_Restore(type, value, tb);
2886+
PyErr_SetObject(type, value);
2887+
/* PyErr_SetObject incref's its arguments */
2888+
Py_XDECREF(value);
2889+
Py_XDECREF(type);
28872890
return WHY_EXCEPTION;
28882891

28892892
raise_error:
28902893
Py_XDECREF(value);
28912894
Py_XDECREF(type);
2892-
Py_XDECREF(tb);
28932895
Py_XDECREF(cause);
28942896
return WHY_EXCEPTION;
28952897
}

Python/errors.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,45 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
5252
void
5353
PyErr_SetObject(PyObject *exception, PyObject *value)
5454
{
55+
PyThreadState *tstate = PyThreadState_GET();
56+
PyObject *tb = NULL;
57+
5558
if (exception != NULL &&
5659
!PyExceptionClass_Check(exception)) {
5760
PyErr_Format(PyExc_SystemError,
5861
"exception %R not a BaseException subclass",
5962
exception);
6063
return;
6164
}
62-
Py_XINCREF(exception);
6365
Py_XINCREF(value);
64-
PyErr_Restore(exception, value, (PyObject *)NULL);
66+
if (tstate->exc_value != NULL && tstate->exc_value != Py_None) {
67+
/* Implicit exception chaining */
68+
if (value == NULL || !PyExceptionInstance_Check(value)) {
69+
/* We must normalize the value right now */
70+
PyObject *args, *fixed_value;
71+
if (value == NULL || value == Py_None)
72+
args = PyTuple_New(0);
73+
else if (PyTuple_Check(value)) {
74+
Py_INCREF(value);
75+
args = value;
76+
}
77+
else
78+
args = PyTuple_Pack(1, value);
79+
fixed_value = args ?
80+
PyEval_CallObject(exception, args) : NULL;
81+
Py_XDECREF(args);
82+
Py_XDECREF(value);
83+
if (fixed_value == NULL)
84+
return;
85+
value = fixed_value;
86+
}
87+
Py_INCREF(tstate->exc_value);
88+
PyException_SetContext(value, tstate->exc_value);
89+
}
90+
if (value != NULL && PyExceptionInstance_Check(value))
91+
tb = PyException_GetTraceback(value);
92+
Py_XINCREF(exception);
93+
PyErr_Restore(exception, value, tb);
6594
}
6695

6796
void

0 commit comments

Comments
 (0)