diff -r 39c18162137b Doc/c-api/exceptions.rst --- a/Doc/c-api/exceptions.rst Mon Mar 05 22:02:28 2012 +0100 +++ b/Doc/c-api/exceptions.rst Mon Mar 05 15:49:19 2012 -0600 @@ -420,30 +420,24 @@ Exception Objects .. c:function:: PyObject* PyException_GetCause(PyObject *ex) Return the cause (either an exception instance, or :const:`None`, set by ``raise ... from ...``) associated with the exception as a new reference, as accessible from Python through :attr:`__cause__`. - If there is no cause associated, this returns *NULL* (from Python - ``__cause__ is Ellipsis``). If the cause is :const:`None`, the default - exception display routines stop showing the context chain. - .. c:function:: void PyException_SetCause(PyObject *ex, PyObject *ctx) Set the cause associated with the exception to *ctx*. Use *NULL* to clear it. There is no type check to make sure that *ctx* is either an exception instance or :const:`None`. This steals a reference to *ctx*. - If the cause is set to :const:`None` the default exception display - routines will not display this exception's context, and will not follow the - chain any further. + :attr:`__suppress_context__` is implicitly set to ``True`` by this function. .. _unicodeexceptions: Unicode Exception Objects ========================= The following functions are used to create and modify Unicode exceptions from C. diff -r 39c18162137b Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst Mon Mar 05 22:02:28 2012 +0100 +++ b/Doc/library/exceptions.rst Mon Mar 05 15:49:19 2012 -0600 @@ -34,28 +34,27 @@ programmers are encouraged to at least d defining exceptions is available in the Python Tutorial under :ref:`tut-userexceptions`. When raising (or re-raising) an exception in an :keyword:`except` clause :attr:`__context__` is automatically set to the last exception caught; if the new exception is not handled the traceback that is eventually displayed will include the originating exception(s) and the final exception. -This implicit exception chain can be made explicit by using :keyword:`from` -with :keyword:`raise`. The single argument to :keyword:`from` must be an -exception or :const:`None`, and it will be set as :attr:`__cause__` on the -raised exception. If :attr:`__cause__` is an exception it will be displayed -instead of :attr:`__context__`; if :attr:`__cause__` is None, -:attr:`__context__` will not be displayed by the default exception handling -code. (Note: the default value for :attr:`__context__` is :const:`None`, -while the default value for :attr:`__cause__` is :const:`Ellipsis`.) +This implicit exception chain can be made explicit by using :keyword:`from` with +:keyword:`raise`. The single argument to :keyword:`from` must be an exception +or ``None``. It will be set as :attr:`__cause__` on the raised exception. +Setting :attr:`__cause__` implicitly sets the :attr:`__suppress_context__` to +``True``. If :attr:`__cause__` is an exception, it will be displayed. If +:attr:`__suppress_context__` has a true value, :attr:`__context__` will not be +displayed. -In either case, the default exception handling code will not display -any of the remaining links in the :attr:`__context__` chain if -:attr:`__cause__` has been set. +In either case, the default exception handling code will not display any of the +remaining links in the :attr:`__context__` chain if :attr:`__cause__` has been +set. Base classes ------------ The following exceptions are used mostly as base classes for other exceptions. .. exception:: BaseException diff -r 39c18162137b Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst Mon Mar 05 22:02:28 2012 +0100 +++ b/Doc/library/stdtypes.rst Mon Mar 05 15:49:19 2012 -0600 @@ -2985,21 +2985,20 @@ supports no special operations. There i It is written as ``None``. .. _bltin-ellipsis-object: The Ellipsis Object ------------------- -This object is commonly used by slicing (see :ref:`slicings`), but may also -be used in other situations where a sentinel value other than :const:`None` -is needed. It supports no special operations. There is exactly one ellipsis -object, named :const:`Ellipsis` (a built-in name). ``type(Ellipsis)()`` -produces the :const:`Ellipsis` singleton. +This object is commonly used by slicing (see :ref:`slicings`). It supports no +special operations. There is exactly one ellipsis object, named +:const:`Ellipsis` (a built-in name). ``type(Ellipsis)()`` produces the +:const:`Ellipsis` singleton. It is written as ``Ellipsis`` or ``...``. .. _bltin-notimplemented-object: The NotImplemented Object ------------------------- diff -r 39c18162137b Include/pyerrors.h --- a/Include/pyerrors.h Mon Mar 05 22:02:28 2012 +0100 +++ b/Include/pyerrors.h Mon Mar 05 15:49:19 2012 -0600 @@ -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 39c18162137b Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py Mon Mar 05 22:02:28 2012 +0100 +++ b/Lib/test/test_exceptions.py Mon Mar 05 15:49:19 2012 -0600 @@ -383,45 +383,46 @@ 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__) + self.assertFalse(e.__suppress_context__) e.__context__ = NameError() e.__cause__ = None self.assertIsInstance(e.__context__, NameError) self.assertIsNone(e.__cause__) - - e.__cause__ = Ellipsis - self.assertIs(e.__cause__, Ellipsis) + self.assertTrue(e.__suppress_context__) + e.__suppress_context__ = False + self.assertFalse(e.__suppress_context__) 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): diff -r 39c18162137b Lib/test/test_raise.py --- a/Lib/test/test_raise.py Mon Mar 05 22:02:28 2012 +0100 +++ b/Lib/test/test_raise.py Mon Mar 05 15:49:19 2012 -0600 @@ -169,21 +169,24 @@ 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 + self.assertTrue(exc.__suppress_context__) + exc.__suppress_context__ = False + raise exc except ValueError as exc: e = exc - self.assertIs(e.__cause__, Ellipsis) + self.assertIsNone(e.__cause__) + self.assertFalse(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 39c18162137b Lib/traceback.py --- a/Lib/traceback.py Mon Mar 05 22:02:28 2012 +0100 +++ b/Lib/traceback.py Mon Mar 05 15:49:19 2012 -0600 @@ -114,25 +114,26 @@ 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 = [] + context = exc.__context__ cause = exc.__cause__ - if cause is Ellipsis: - context = exc.__context__ - if 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: + if cause is not None and cause not in seen: its.append(_iter_chain(cause, False, seen)) its.append([(_cause_message, None)]) + if (context is not None and + not exc.__suppress_context__ + and context not in seen): + its.append(_iter_chain(context, None, seen)) + its.append([(_context_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 39c18162137b Objects/exceptions.c --- a/Objects/exceptions.c Mon Mar 05 22:02:28 2012 +0100 +++ b/Objects/exceptions.c Mon Mar 05 15:49:19 2012 -0600 @@ -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, @@ -328,16 +322,17 @@ PyException_GetCause(PyObject *self) { return cause; } /* Steals a reference to cause */ void PyException_SetCause(PyObject *self, PyObject *cause) { PyObject *old_cause = ((PyBaseExceptionObject *)self)->cause; ((PyBaseExceptionObject *)self)->cause = cause; + ((PyBaseExceptionObject *)self)->suppress_context = 1; Py_XDECREF(old_cause); } PyObject * PyException_GetContext(PyObject *self) { PyObject *context = ((PyBaseExceptionObject *)self)->context; Py_XINCREF(context); return context; @@ -347,16 +342,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 +378,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 39c18162137b Python/ceval.c --- a/Python/ceval.c Mon Mar 05 22:02:28 2012 +0100 +++ b/Python/ceval.c Mon Mar 05 15:49:19 2012 -0600 @@ -3562,33 +3562,36 @@ 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; + } + 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 39c18162137b Python/pythonrun.c --- a/Python/pythonrun.c Mon Mar 05 22:02:28 2012 +0100 +++ b/Python/pythonrun.c Mon Mar 05 15:49:19 2012 -0600 @@ -1693,32 +1693,29 @@ print_exception_recursive(PyObject *f, P 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 */ - ; - } - else if (cause) { + 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) { + if (context && + !((PyBaseExceptionObject *)value)->suppress_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);