diff -r 98a1855ebfe1 Include/pyerrors.h --- a/Include/pyerrors.h Sun Feb 26 01:54:46 2012 -0800 +++ b/Include/pyerrors.h Sun Feb 26 10:18:21 2012 -0500 @@ -100,17 +100,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 98a1855ebfe1 Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py Sun Feb 26 01:54:46 2012 -0800 +++ b/Lib/test/test_exceptions.py Sun Feb 26 10:18:21 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 98a1855ebfe1 Lib/test/test_raise.py --- a/Lib/test/test_raise.py Sun Feb 26 01:54:46 2012 -0800 +++ b/Lib/test/test_raise.py Sun Feb 26 10:18:21 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 98a1855ebfe1 Lib/traceback.py --- a/Lib/traceback.py Sun Feb 26 01:54:46 2012 -0800 +++ b/Lib/traceback.py Sun Feb 26 10:18:21 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 98a1855ebfe1 Objects/exceptions.c --- a/Objects/exceptions.c Sun Feb 26 01:54:46 2012 -0800 +++ b/Objects/exceptions.c Sun Feb 26 10:18:21 2012 -0500 @@ -261,43 +261,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, diff -r 98a1855ebfe1 Python/ceval.c --- a/Python/ceval.c Sun Feb 26 01:54:46 2012 -0800 +++ b/Python/ceval.c Sun Feb 26 10:18:21 2012 -0500 @@ -3562,33 +3562,41 @@ 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_IDENTIFIER(__suppress_context__); + int res; + Py_DECREF(cause); + fixed_cause = NULL; + res = _PyObject_SetAttrId(value, &PyId___suppress_context__, Py_True); + if (res == -1) + goto raise_error; + } + 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 98a1855ebfe1 Python/pythonrun.c --- a/Python/pythonrun.c Sun Feb 26 01:54:46 2012 -0800 +++ b/Python/pythonrun.c Sun Feb 26 10:18:21 2012 -0500 @@ -1683,54 +1683,66 @@ static const char *cause_message = static const char *context_message = "\nDuring handling of the above exception, " "another exception occurred:\n\n"; static void print_exception_recursive(PyObject *f, PyObject *value, PyObject *seen) { - int err = 0, res; - PyObject *cause, *context; + int err = 0, res, print; + PyObject *cause, *context, *sup; + _Py_IDENTIFIER(__suppress_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 */ - ; + print = 1; + sup = _PyObject_GetAttrId(value, &PyId___suppress_context__); + if (sup == NULL) { + PyErr_Clear(); } - else if (cause) { - res = PySet_Contains(seen, cause); + else { + res = PyObject_IsTrue(sup); + Py_DECREF(sup); if (res == -1) PyErr_Clear(); - if (res == 0) { - print_exception_recursive( - f, cause, seen); - err |= PyFile_WriteString( - cause_message, f); + else + print = !res; + } + if (print) { + 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 (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