diff -r 1d7472b015f0 Include/pyerrors.h --- a/Include/pyerrors.h Sun Feb 26 18:09:50 2012 +0100 +++ b/Include/pyerrors.h Sun Feb 26 15:01:03 2012 -0500 @@ -5,17 +5,18 @@ extern "C" { #endif /* Error objects */ #ifndef Py_LIMITED_API /* PyException_HEAD defines the initial segment of every exception class. */ #define PyException_HEAD PyObject_HEAD PyObject *dict;\ PyObject *args; PyObject *traceback;\ - PyObject *context; PyObject *cause; + PyObject *context; PyObject *cause;\ + int suppress_context; typedef struct { PyException_HEAD } PyBaseExceptionObject; typedef struct { PyException_HEAD PyObject *msg; @@ -100,17 +101,16 @@ PyAPI_FUNC(void) PyErr_NormalizeExceptio /* Traceback manipulation (PEP 3134) */ PyAPI_FUNC(int) PyException_SetTraceback(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyException_GetTraceback(PyObject *); /* Cause manipulation (PEP 3134) */ PyAPI_FUNC(PyObject *) PyException_GetCause(PyObject *); PyAPI_FUNC(void) PyException_SetCause(PyObject *, PyObject *); -PyAPI_FUNC(int) _PyException_SetCauseChecked(PyObject *, PyObject *); /* Context manipulation (PEP 3134) */ PyAPI_FUNC(PyObject *) PyException_GetContext(PyObject *); PyAPI_FUNC(void) PyException_SetContext(PyObject *, PyObject *); /* */ diff -r 1d7472b015f0 Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py Sun Feb 26 18:09:50 2012 +0100 +++ b/Lib/test/test_exceptions.py Sun Feb 26 15:01:03 2012 -0500 @@ -383,46 +383,43 @@ class ExceptionTests(unittest.TestCase): e = Exception() e.__traceback__ = tb e.__traceback__ = None self.assertEqual(e.__traceback__, None) def testChainingAttrs(self): e = Exception() self.assertIsNone(e.__context__) - self.assertIs(e.__cause__, Ellipsis) + self.assertIsNone(e.__cause__) e = TypeError() self.assertIsNone(e.__context__) - self.assertIs(e.__cause__, Ellipsis) + self.assertIsNone(e.__cause__) class MyException(EnvironmentError): pass e = MyException() self.assertIsNone(e.__context__) - self.assertIs(e.__cause__, Ellipsis) + self.assertIsNone(e.__cause__) def testChainingDescriptors(self): try: raise Exception() except Exception as exc: e = exc self.assertIsNone(e.__context__) - self.assertIs(e.__cause__, Ellipsis) + self.assertIsNone(e.__cause__) e.__context__ = NameError() e.__cause__ = None self.assertIsInstance(e.__context__, NameError) self.assertIsNone(e.__cause__) - e.__cause__ = Ellipsis - self.assertIs(e.__cause__, Ellipsis) - def testKeywordArgs(self): # test that builtin exception don't take keyword args, # but user-defined subclasses can if they want self.assertRaises(TypeError, BaseException, a=1) class DerivedException(BaseException): def __init__(self, fancy_arg): BaseException.__init__(self) diff -r 1d7472b015f0 Lib/test/test_raise.py --- a/Lib/test/test_raise.py Sun Feb 26 18:09:50 2012 +0100 +++ b/Lib/test/test_raise.py Sun Feb 26 15:01:03 2012 -0500 @@ -169,21 +169,22 @@ class TestCause(unittest.TestCase): try: try: try: raise TypeError except Exception: raise ValueError from None except ValueError as exc: self.assertIsNone(exc.__cause__) - raise exc from Ellipsis + raise exc except ValueError as exc: e = exc - self.assertIs(e.__cause__, Ellipsis) + self.assertIsNone(e.__cause__) + self.assertTrue(e.__suppress_context__) self.assertIsInstance(e.__context__, TypeError) def test_invalid_cause(self): try: raise IndexError from 5 except TypeError as e: self.assertIn("exception cause", str(e)) else: diff -r 1d7472b015f0 Lib/traceback.py --- a/Lib/traceback.py Sun Feb 26 18:09:50 2012 +0100 +++ b/Lib/traceback.py Sun Feb 26 15:01:03 2012 -0500 @@ -114,25 +114,25 @@ def extract_tb(tb, limit=None): "\nDuring handling of the above exception, " "another exception occurred:\n") def _iter_chain(exc, custom_tb=None, seen=None): if seen is None: seen = set() seen.add(exc) its = [] - cause = exc.__cause__ - if cause is Ellipsis: + if not getattr(exc, "__suppress_context__", False): context = exc.__context__ - if context is not None and context not in seen: + cause = exc.__cause__ + if cause is not None and cause not in seen: + its.append(_iter_chain(cause, False, seen)) + its.append([(_cause_message, None)]) + elif context is not None and context not in seen: its.append(_iter_chain(context, None, seen)) its.append([(_context_message, None)]) - elif cause is not None and cause not in seen: - its.append(_iter_chain(cause, False, seen)) - its.append([(_cause_message, None)]) its.append([(exc, custom_tb or exc.__traceback__)]) # itertools.chain is in an extension module and may be unavailable for it in its: for x in it: yield x def print_exception(etype, value, tb, limit=None, file=None, chain=True): diff -r 1d7472b015f0 Objects/exceptions.c --- a/Objects/exceptions.c Sun Feb 26 18:09:50 2012 +0100 +++ b/Objects/exceptions.c Sun Feb 26 15:01:03 2012 -0500 @@ -37,16 +37,17 @@ BaseException_new(PyTypeObject *type, Py PyBaseExceptionObject *self; self = (PyBaseExceptionObject *)type->tp_alloc(type, 0); if (!self) return NULL; /* the dict is created on the fly in PyObject_GenericSetAttr */ self->dict = NULL; self->traceback = self->cause = self->context = NULL; + self->suppress_context = 0; self->args = PyTuple_New(0); if (!self->args) { Py_DECREF(self); return NULL; } return (PyObject *)self; @@ -261,43 +262,36 @@ BaseException_set_context(PyObject *self return 0; } static PyObject * BaseException_get_cause(PyObject *self) { PyObject *res = PyException_GetCause(self); if (res) return res; /* new reference already returned above */ - Py_INCREF(Py_Ellipsis); - return Py_Ellipsis; -} - -int -_PyException_SetCauseChecked(PyObject *self, PyObject *arg) { - if (arg == Py_Ellipsis) { - arg = NULL; - } else if (arg != Py_None && !PyExceptionInstance_Check(arg)) { - PyErr_SetString(PyExc_TypeError, "exception cause must be None, " - "Ellipsis or derive from BaseException"); - return -1; - } else { - /* PyException_SetCause steals a reference */ - Py_INCREF(arg); - } - PyException_SetCause(self, arg); - return 0; + Py_RETURN_NONE; } static int BaseException_set_cause(PyObject *self, PyObject *arg) { if (arg == NULL) { PyErr_SetString(PyExc_TypeError, "__cause__ may not be deleted"); return -1; + } else if (arg == Py_None) { + arg = NULL; + } else if (!PyExceptionInstance_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "exception cause must be None " + "or derive from BaseException"); + return -1; + } else { + /* PyException_SetCause steals this reference */ + Py_INCREF(arg); } - return _PyException_SetCauseChecked(self, arg); + PyException_SetCause(self, arg); + return 0; } static PyGetSetDef BaseException_getset[] = { {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, {"args", (getter)BaseException_get_args, (setter)BaseException_set_args}, {"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb}, {"__context__", (getter)BaseException_get_context, @@ -347,16 +341,22 @@ PyException_GetContext(PyObject *self) { void PyException_SetContext(PyObject *self, PyObject *context) { PyObject *old_context = ((PyBaseExceptionObject *)self)->context; ((PyBaseExceptionObject *)self)->context = context; Py_XDECREF(old_context); } +static struct PyMemberDef BaseException_members[] = { + {"__suppress_context__", T_BOOL, + offsetof(PyBaseExceptionObject, suppress_context)} +}; + + static PyTypeObject _PyExc_BaseException = { PyVarObject_HEAD_INIT(NULL, 0) "BaseException", /*tp_name*/ sizeof(PyBaseExceptionObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)BaseException_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ @@ -377,17 +377,17 @@ static PyTypeObject _PyExc_BaseException PyDoc_STR("Common base class for all exceptions"), /* tp_doc */ (traverseproc)BaseException_traverse, /* tp_traverse */ (inquiry)BaseException_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ BaseException_methods, /* tp_methods */ - 0, /* tp_members */ + BaseException_members, /* tp_members */ BaseException_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof(PyBaseExceptionObject, dict), /* tp_dictoffset */ (initproc)BaseException_init, /* tp_init */ 0, /* tp_alloc */ diff -r 1d7472b015f0 Python/ceval.c --- a/Python/ceval.c Sun Feb 26 18:09:50 2012 +0100 +++ b/Python/ceval.c Sun Feb 26 15:01:03 2012 -0500 @@ -3562,33 +3562,37 @@ do_raise(PyObject *exc, PyObject *cause) Py_DECREF(exc); PyErr_SetString(PyExc_TypeError, "exceptions must derive from BaseException"); goto raise_error; } if (cause) { PyObject *fixed_cause; - int result; if (PyExceptionClass_Check(cause)) { fixed_cause = PyObject_CallObject(cause, NULL); if (fixed_cause == NULL) goto raise_error; - Py_CLEAR(cause); - } else { - /* Let "exc.__cause__ = cause" handle all further checks */ + Py_DECREF(cause); + } + else if (PyExceptionInstance_Check(cause)) { fixed_cause = cause; - cause = NULL; /* Steal the reference */ } - /* We retain ownership of the reference to fixed_cause */ - result = _PyException_SetCauseChecked(value, fixed_cause); - Py_DECREF(fixed_cause); - if (result < 0) { + else if (cause == Py_None) { + Py_DECREF(cause); + fixed_cause = NULL; + ((PyBaseExceptionObject *)value)->suppress_context = 1; + } + else { + PyErr_SetString(PyExc_TypeError, + "exception causes must derive from " + "BaseException"); goto raise_error; } + PyException_SetCause(value, fixed_cause); } PyErr_SetObject(type, value); /* PyErr_SetObject incref's its arguments */ Py_XDECREF(value); Py_XDECREF(type); return WHY_EXCEPTION; diff -r 1d7472b015f0 Python/pythonrun.c --- a/Python/pythonrun.c Sun Feb 26 18:09:50 2012 +0100 +++ b/Python/pythonrun.c Sun Feb 26 15:01:03 2012 -0500 @@ -1691,46 +1691,44 @@ print_exception_recursive(PyObject *f, P int err = 0, res; PyObject *cause, *context; if (seen != NULL) { /* Exception chaining */ if (PySet_Add(seen, value) == -1) PyErr_Clear(); else if (PyExceptionInstance_Check(value)) { - cause = PyException_GetCause(value); - context = PyException_GetContext(value); - if (cause && cause == Py_None) { - /* print neither cause nor context */ - ; + if (!((PyBaseExceptionObject *)value)->suppress_context) { + cause = PyException_GetCause(value); + context = PyException_GetContext(value); + if (cause) { + res = PySet_Contains(seen, cause); + if (res == -1) + PyErr_Clear(); + if (res == 0) { + print_exception_recursive( + f, cause, seen); + err |= PyFile_WriteString( + cause_message, f); + } + } + else if (context) { + res = PySet_Contains(seen, context); + if (res == -1) + PyErr_Clear(); + if (res == 0) { + print_exception_recursive( + f, context, seen); + err |= PyFile_WriteString( + context_message, f); + } + } + Py_XDECREF(context); + Py_XDECREF(cause); } - else if (cause) { - res = PySet_Contains(seen, cause); - if (res == -1) - PyErr_Clear(); - if (res == 0) { - print_exception_recursive( - f, cause, seen); - err |= PyFile_WriteString( - cause_message, f); - } - } - else if (context) { - res = PySet_Contains(seen, context); - if (res == -1) - PyErr_Clear(); - if (res == 0) { - print_exception_recursive( - f, context, seen); - err |= PyFile_WriteString( - context_message, f); - } - } - Py_XDECREF(context); - Py_XDECREF(cause); } } print_exception(f, value); if (err != 0) PyErr_Clear(); } void