mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	PEP 415: Implement suppression of __context__ display with an exception attribute
This replaces the original PEP 409 implementation. See #14133.
This commit is contained in:
		
							parent
							
								
									d91dc62379
								
							
						
					
					
						commit
						d5a1c44455
					
				
					 12 changed files with 80 additions and 79 deletions
				
			
		| 
						 | 
					@ -471,10 +471,6 @@ Exception Objects
 | 
				
			||||||
   set by ``raise ... from ...``) associated with the exception as a new
 | 
					   set by ``raise ... from ...``) associated with the exception as a new
 | 
				
			||||||
   reference, as accessible from Python through :attr:`__cause__`.
 | 
					   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)
 | 
					.. c:function:: void PyException_SetCause(PyObject *ex, PyObject *ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -482,9 +478,7 @@ Exception Objects
 | 
				
			||||||
   it.  There is no type check to make sure that *ctx* is either an exception
 | 
					   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*.
 | 
					   instance or :const:`None`.  This steals a reference to *ctx*.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   If the cause is set to :const:`None` the default exception display
 | 
					   :attr:`__suppress_context__` is implicitly set to ``True`` by this function.
 | 
				
			||||||
   routines will not display this exception's context, and will not follow the
 | 
					 | 
				
			||||||
   chain any further.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. _unicodeexceptions:
 | 
					.. _unicodeexceptions:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,18 +39,17 @@ When raising (or re-raising) an exception in an :keyword:`except` clause
 | 
				
			||||||
new exception is not handled the traceback that is eventually displayed will
 | 
					new exception is not handled the traceback that is eventually displayed will
 | 
				
			||||||
include the originating exception(s) and the final exception.
 | 
					include the originating exception(s) and the final exception.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This implicit exception chain can be made explicit by using :keyword:`from`
 | 
					This implicit exception chain can be made explicit by using :keyword:`from` with
 | 
				
			||||||
with :keyword:`raise`.  The single argument to :keyword:`from` must be an
 | 
					:keyword:`raise`.  The single argument to :keyword:`from` must be an exception
 | 
				
			||||||
exception or :const:`None`, and it will be set as :attr:`__cause__` on the
 | 
					or ``None``. It will be set as :attr:`__cause__` on the raised exception.
 | 
				
			||||||
raised exception.  If :attr:`__cause__` is an exception it will be displayed
 | 
					Setting :attr:`__cause__` implicitly sets the :attr:`__suppress_context__` to
 | 
				
			||||||
instead of :attr:`__context__`; if :attr:`__cause__` is None,
 | 
					``True``. If :attr:`__cause__` is an exception, it will be displayed. If
 | 
				
			||||||
:attr:`__context__` will not be displayed by the default exception handling
 | 
					:attr:`__cause__` is present or :attr:`__suppress_context__` has a true value,
 | 
				
			||||||
code.  (Note:  the default value for :attr:`__context__` is :const:`None`,
 | 
					:attr:`__context__` will not be displayed.
 | 
				
			||||||
while the default value for :attr:`__cause__` is :const:`Ellipsis`.)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
In either case, the default exception handling code will not display
 | 
					In either case, the default exception handling code will not display any of the
 | 
				
			||||||
any of the remaining links in the :attr:`__context__` chain if
 | 
					remaining links in the :attr:`__context__` chain if :attr:`__cause__` has been
 | 
				
			||||||
:attr:`__cause__` has been set.
 | 
					set.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Base classes
 | 
					Base classes
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2996,11 +2996,10 @@ It is written as ``None``.
 | 
				
			||||||
The Ellipsis Object
 | 
					The Ellipsis Object
 | 
				
			||||||
-------------------
 | 
					-------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This object is commonly used by slicing (see :ref:`slicings`), but may also
 | 
					This object is commonly used by slicing (see :ref:`slicings`).  It supports no
 | 
				
			||||||
be used in other situations where a sentinel value other than :const:`None`
 | 
					special operations.  There is exactly one ellipsis object, named
 | 
				
			||||||
is needed.  It supports no special operations.  There is exactly one ellipsis
 | 
					:const:`Ellipsis` (a built-in name).  ``type(Ellipsis)()`` produces the
 | 
				
			||||||
object, named :const:`Ellipsis` (a built-in name).  ``type(Ellipsis)()``
 | 
					:const:`Ellipsis` singleton.
 | 
				
			||||||
produces the :const:`Ellipsis` singleton.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
It is written as ``Ellipsis`` or ``...``.
 | 
					It is written as ``Ellipsis`` or ``...``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,8 @@ extern "C" {
 | 
				
			||||||
/* PyException_HEAD defines the initial segment of every exception class. */
 | 
					/* PyException_HEAD defines the initial segment of every exception class. */
 | 
				
			||||||
#define PyException_HEAD PyObject_HEAD PyObject *dict;\
 | 
					#define PyException_HEAD PyObject_HEAD PyObject *dict;\
 | 
				
			||||||
             PyObject *args; PyObject *traceback;\
 | 
					             PyObject *args; PyObject *traceback;\
 | 
				
			||||||
             PyObject *context; PyObject *cause;
 | 
					             PyObject *context; PyObject *cause;\
 | 
				
			||||||
 | 
					             int suppress_context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    PyException_HEAD
 | 
					    PyException_HEAD
 | 
				
			||||||
| 
						 | 
					@ -114,7 +115,6 @@ PyAPI_FUNC(PyObject *) PyException_GetTraceback(PyObject *);
 | 
				
			||||||
/* Cause manipulation (PEP 3134) */
 | 
					/* Cause manipulation (PEP 3134) */
 | 
				
			||||||
PyAPI_FUNC(PyObject *) PyException_GetCause(PyObject *);
 | 
					PyAPI_FUNC(PyObject *) PyException_GetCause(PyObject *);
 | 
				
			||||||
PyAPI_FUNC(void) PyException_SetCause(PyObject *, PyObject *);
 | 
					PyAPI_FUNC(void) PyException_SetCause(PyObject *, PyObject *);
 | 
				
			||||||
PyAPI_FUNC(int) _PyException_SetCauseChecked(PyObject *, PyObject *);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Context manipulation (PEP 3134) */
 | 
					/* Context manipulation (PEP 3134) */
 | 
				
			||||||
PyAPI_FUNC(PyObject *) PyException_GetContext(PyObject *);
 | 
					PyAPI_FUNC(PyObject *) PyException_GetContext(PyObject *);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -388,18 +388,18 @@ def testNoneClearsTracebackAttr(self):
 | 
				
			||||||
    def testChainingAttrs(self):
 | 
					    def testChainingAttrs(self):
 | 
				
			||||||
        e = Exception()
 | 
					        e = Exception()
 | 
				
			||||||
        self.assertIsNone(e.__context__)
 | 
					        self.assertIsNone(e.__context__)
 | 
				
			||||||
        self.assertIs(e.__cause__, Ellipsis)
 | 
					        self.assertIsNone(e.__cause__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        e = TypeError()
 | 
					        e = TypeError()
 | 
				
			||||||
        self.assertIsNone(e.__context__)
 | 
					        self.assertIsNone(e.__context__)
 | 
				
			||||||
        self.assertIs(e.__cause__, Ellipsis)
 | 
					        self.assertIsNone(e.__cause__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class MyException(EnvironmentError):
 | 
					        class MyException(EnvironmentError):
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        e = MyException()
 | 
					        e = MyException()
 | 
				
			||||||
        self.assertIsNone(e.__context__)
 | 
					        self.assertIsNone(e.__context__)
 | 
				
			||||||
        self.assertIs(e.__cause__, Ellipsis)
 | 
					        self.assertIsNone(e.__cause__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def testChainingDescriptors(self):
 | 
					    def testChainingDescriptors(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					@ -408,15 +408,16 @@ def testChainingDescriptors(self):
 | 
				
			||||||
            e = exc
 | 
					            e = exc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertIsNone(e.__context__)
 | 
					        self.assertIsNone(e.__context__)
 | 
				
			||||||
        self.assertIs(e.__cause__, Ellipsis)
 | 
					        self.assertIsNone(e.__cause__)
 | 
				
			||||||
 | 
					        self.assertFalse(e.__suppress_context__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        e.__context__ = NameError()
 | 
					        e.__context__ = NameError()
 | 
				
			||||||
        e.__cause__ = None
 | 
					        e.__cause__ = None
 | 
				
			||||||
        self.assertIsInstance(e.__context__, NameError)
 | 
					        self.assertIsInstance(e.__context__, NameError)
 | 
				
			||||||
        self.assertIsNone(e.__cause__)
 | 
					        self.assertIsNone(e.__cause__)
 | 
				
			||||||
 | 
					        self.assertTrue(e.__suppress_context__)
 | 
				
			||||||
        e.__cause__ = Ellipsis
 | 
					        e.__suppress_context__ = False
 | 
				
			||||||
        self.assertIs(e.__cause__, Ellipsis)
 | 
					        self.assertFalse(e.__suppress_context__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def testKeywordArgs(self):
 | 
					    def testKeywordArgs(self):
 | 
				
			||||||
        # test that builtin exception don't take keyword args,
 | 
					        # test that builtin exception don't take keyword args,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -174,11 +174,14 @@ def testCauseSyntax(self):
 | 
				
			||||||
                    raise ValueError from None
 | 
					                    raise ValueError from None
 | 
				
			||||||
            except ValueError as exc:
 | 
					            except ValueError as exc:
 | 
				
			||||||
                self.assertIsNone(exc.__cause__)
 | 
					                self.assertIsNone(exc.__cause__)
 | 
				
			||||||
                raise exc from Ellipsis
 | 
					                self.assertTrue(exc.__suppress_context__)
 | 
				
			||||||
 | 
					                exc.__suppress_context__ = False
 | 
				
			||||||
 | 
					                raise exc
 | 
				
			||||||
        except ValueError as exc:
 | 
					        except ValueError as exc:
 | 
				
			||||||
            e = exc
 | 
					            e = exc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertIs(e.__cause__, Ellipsis)
 | 
					        self.assertIsNone(e.__cause__)
 | 
				
			||||||
 | 
					        self.assertFalse(e.__suppress_context__)
 | 
				
			||||||
        self.assertIsInstance(e.__context__, TypeError)
 | 
					        self.assertIsInstance(e.__context__, TypeError)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_invalid_cause(self):
 | 
					    def test_invalid_cause(self):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -700,14 +700,14 @@ def inner():
 | 
				
			||||||
        class C(object): pass
 | 
					        class C(object): pass
 | 
				
			||||||
        check(C.__dict__, size(h + 'P'))
 | 
					        check(C.__dict__, size(h + 'P'))
 | 
				
			||||||
        # BaseException
 | 
					        # BaseException
 | 
				
			||||||
        check(BaseException(), size(h + '5P'))
 | 
					        check(BaseException(), size(h + '5Pi'))
 | 
				
			||||||
        # UnicodeEncodeError
 | 
					        # UnicodeEncodeError
 | 
				
			||||||
        check(UnicodeEncodeError("", "", 0, 0, ""), size(h + '5P 2P2PP'))
 | 
					        check(UnicodeEncodeError("", "", 0, 0, ""), size(h + '5Pi 2P2PP'))
 | 
				
			||||||
        # UnicodeDecodeError
 | 
					        # UnicodeDecodeError
 | 
				
			||||||
        # XXX
 | 
					        # XXX
 | 
				
			||||||
#        check(UnicodeDecodeError("", "", 0, 0, ""), size(h + '5P2PP'))
 | 
					#        check(UnicodeDecodeError("", "", 0, 0, ""), size(h + '5P2PP'))
 | 
				
			||||||
        # UnicodeTranslateError
 | 
					        # UnicodeTranslateError
 | 
				
			||||||
        check(UnicodeTranslateError("", 0, 1, ""), size(h + '5P 2P2PP'))
 | 
					        check(UnicodeTranslateError("", 0, 1, ""), size(h + '5Pi 2P2PP'))
 | 
				
			||||||
        # ellipses
 | 
					        # ellipses
 | 
				
			||||||
        check(Ellipsis, size(h + ''))
 | 
					        check(Ellipsis, size(h + ''))
 | 
				
			||||||
        # EncodingMap
 | 
					        # EncodingMap
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -119,15 +119,16 @@ def _iter_chain(exc, custom_tb=None, seen=None):
 | 
				
			||||||
        seen = set()
 | 
					        seen = set()
 | 
				
			||||||
    seen.add(exc)
 | 
					    seen.add(exc)
 | 
				
			||||||
    its = []
 | 
					    its = []
 | 
				
			||||||
    cause = exc.__cause__
 | 
					 | 
				
			||||||
    if cause is Ellipsis:
 | 
					 | 
				
			||||||
    context = exc.__context__
 | 
					    context = exc.__context__
 | 
				
			||||||
        if context is not None and context not in seen:
 | 
					    cause = exc.__cause__
 | 
				
			||||||
            its.append(_iter_chain(context, None, seen))
 | 
					    if cause is not None and cause not in 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(_iter_chain(cause, False, seen))
 | 
				
			||||||
        its.append([(_cause_message, None)])
 | 
					        its.append([(_cause_message, None)])
 | 
				
			||||||
 | 
					    elif (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__)])
 | 
					    its.append([(exc, custom_tb or exc.__traceback__)])
 | 
				
			||||||
    # itertools.chain is in an extension module and may be unavailable
 | 
					    # itertools.chain is in an extension module and may be unavailable
 | 
				
			||||||
    for it in its:
 | 
					    for it in its:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,9 @@ What's New in Python 3.3.0 Alpha 4?
 | 
				
			||||||
Core and Builtins
 | 
					Core and Builtins
 | 
				
			||||||
-----------------
 | 
					-----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Issue #14133 (PEP 415): Implement suppression of __context__ display with an
 | 
				
			||||||
 | 
					  attribute on BaseException. This replaces the original mechanism of PEP 409.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Issue #14417: Mutating a dict during lookup now restarts the lookup instead
 | 
					- Issue #14417: Mutating a dict during lookup now restarts the lookup instead
 | 
				
			||||||
  of raising a RuntimeError (undoes issue #14205).
 | 
					  of raising a RuntimeError (undoes issue #14205).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,6 +42,7 @@ BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | 
				
			||||||
    /* the dict is created on the fly in PyObject_GenericSetAttr */
 | 
					    /* the dict is created on the fly in PyObject_GenericSetAttr */
 | 
				
			||||||
    self->dict = NULL;
 | 
					    self->dict = NULL;
 | 
				
			||||||
    self->traceback = self->cause = self->context = NULL;
 | 
					    self->traceback = self->cause = self->context = NULL;
 | 
				
			||||||
 | 
					    self->suppress_context = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self->args = PyTuple_New(0);
 | 
					    self->args = PyTuple_New(0);
 | 
				
			||||||
    if (!self->args) {
 | 
					    if (!self->args) {
 | 
				
			||||||
| 
						 | 
					@ -266,24 +267,7 @@ BaseException_get_cause(PyObject *self) {
 | 
				
			||||||
    PyObject *res = PyException_GetCause(self);
 | 
					    PyObject *res = PyException_GetCause(self);
 | 
				
			||||||
    if (res)
 | 
					    if (res)
 | 
				
			||||||
        return res;  /* new reference already returned above */
 | 
					        return res;  /* new reference already returned above */
 | 
				
			||||||
    Py_INCREF(Py_Ellipsis);
 | 
					    Py_RETURN_NONE;
 | 
				
			||||||
    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;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
| 
						 | 
					@ -291,8 +275,18 @@ BaseException_set_cause(PyObject *self, PyObject *arg) {
 | 
				
			||||||
    if (arg == NULL) {
 | 
					    if (arg == NULL) {
 | 
				
			||||||
        PyErr_SetString(PyExc_TypeError, "__cause__ may not be deleted");
 | 
					        PyErr_SetString(PyExc_TypeError, "__cause__ may not be deleted");
 | 
				
			||||||
        return -1;
 | 
					        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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -333,6 +327,7 @@ void
 | 
				
			||||||
PyException_SetCause(PyObject *self, PyObject *cause) {
 | 
					PyException_SetCause(PyObject *self, PyObject *cause) {
 | 
				
			||||||
    PyObject *old_cause = ((PyBaseExceptionObject *)self)->cause;
 | 
					    PyObject *old_cause = ((PyBaseExceptionObject *)self)->cause;
 | 
				
			||||||
    ((PyBaseExceptionObject *)self)->cause = cause;
 | 
					    ((PyBaseExceptionObject *)self)->cause = cause;
 | 
				
			||||||
 | 
					    ((PyBaseExceptionObject *)self)->suppress_context = 1;
 | 
				
			||||||
    Py_XDECREF(old_cause);
 | 
					    Py_XDECREF(old_cause);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -352,6 +347,12 @@ PyException_SetContext(PyObject *self, PyObject *context) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct PyMemberDef BaseException_members[] = {
 | 
				
			||||||
 | 
					    {"__suppress_context__", T_BOOL,
 | 
				
			||||||
 | 
					     offsetof(PyBaseExceptionObject, suppress_context)}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static PyTypeObject _PyExc_BaseException = {
 | 
					static PyTypeObject _PyExc_BaseException = {
 | 
				
			||||||
    PyVarObject_HEAD_INIT(NULL, 0)
 | 
					    PyVarObject_HEAD_INIT(NULL, 0)
 | 
				
			||||||
    "BaseException", /*tp_name*/
 | 
					    "BaseException", /*tp_name*/
 | 
				
			||||||
| 
						 | 
					@ -382,7 +383,7 @@ static PyTypeObject _PyExc_BaseException = {
 | 
				
			||||||
    0,                          /* tp_iter */
 | 
					    0,                          /* tp_iter */
 | 
				
			||||||
    0,                          /* tp_iternext */
 | 
					    0,                          /* tp_iternext */
 | 
				
			||||||
    BaseException_methods,      /* tp_methods */
 | 
					    BaseException_methods,      /* tp_methods */
 | 
				
			||||||
    0,                          /* tp_members */
 | 
					    BaseException_members,      /* tp_members */
 | 
				
			||||||
    BaseException_getset,       /* tp_getset */
 | 
					    BaseException_getset,       /* tp_getset */
 | 
				
			||||||
    0,                          /* tp_base */
 | 
					    0,                          /* tp_base */
 | 
				
			||||||
    0,                          /* tp_dict */
 | 
					    0,                          /* tp_dict */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3572,23 +3572,26 @@ do_raise(PyObject *exc, PyObject *cause)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (cause) {
 | 
					    if (cause) {
 | 
				
			||||||
        PyObject *fixed_cause;
 | 
					        PyObject *fixed_cause;
 | 
				
			||||||
        int result;
 | 
					 | 
				
			||||||
        if (PyExceptionClass_Check(cause)) {
 | 
					        if (PyExceptionClass_Check(cause)) {
 | 
				
			||||||
            fixed_cause = PyObject_CallObject(cause, NULL);
 | 
					            fixed_cause = PyObject_CallObject(cause, NULL);
 | 
				
			||||||
            if (fixed_cause == NULL)
 | 
					            if (fixed_cause == NULL)
 | 
				
			||||||
                goto raise_error;
 | 
					                goto raise_error;
 | 
				
			||||||
            Py_CLEAR(cause);
 | 
					            Py_DECREF(cause);
 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            /* Let "exc.__cause__ = cause" handle all further checks */
 | 
					 | 
				
			||||||
            fixed_cause = cause;
 | 
					 | 
				
			||||||
            cause = NULL; /* Steal the reference */
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        /* We retain ownership of the reference to fixed_cause */
 | 
					        else if (PyExceptionInstance_Check(cause)) {
 | 
				
			||||||
        result = _PyException_SetCauseChecked(value, fixed_cause);
 | 
					            fixed_cause = 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;
 | 
					            goto raise_error;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        PyException_SetCause(value, fixed_cause);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    PyErr_SetObject(type, value);
 | 
					    PyErr_SetObject(type, value);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1761,11 +1761,7 @@ print_exception_recursive(PyObject *f, PyObject *value, PyObject *seen)
 | 
				
			||||||
        else if (PyExceptionInstance_Check(value)) {
 | 
					        else if (PyExceptionInstance_Check(value)) {
 | 
				
			||||||
            cause = PyException_GetCause(value);
 | 
					            cause = PyException_GetCause(value);
 | 
				
			||||||
            context = PyException_GetContext(value);
 | 
					            context = PyException_GetContext(value);
 | 
				
			||||||
            if (cause && cause == Py_None) {
 | 
					            if (cause) {
 | 
				
			||||||
                /* print neither cause nor context */
 | 
					 | 
				
			||||||
                ;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (cause) {
 | 
					 | 
				
			||||||
                res = PySet_Contains(seen, cause);
 | 
					                res = PySet_Contains(seen, cause);
 | 
				
			||||||
                if (res == -1)
 | 
					                if (res == -1)
 | 
				
			||||||
                    PyErr_Clear();
 | 
					                    PyErr_Clear();
 | 
				
			||||||
| 
						 | 
					@ -1776,7 +1772,8 @@ print_exception_recursive(PyObject *f, PyObject *value, PyObject *seen)
 | 
				
			||||||
                        cause_message, f);
 | 
					                        cause_message, f);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else if (context) {
 | 
					            else if (context &&
 | 
				
			||||||
 | 
					                !((PyBaseExceptionObject *)value)->suppress_context) {
 | 
				
			||||||
                res = PySet_Contains(seen, context);
 | 
					                res = PySet_Contains(seen, context);
 | 
				
			||||||
                if (res == -1)
 | 
					                if (res == -1)
 | 
				
			||||||
                    PyErr_Clear();
 | 
					                    PyErr_Clear();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue