PEP 415: Implement suppression of __context__ display with an exception attribute

This replaces the original PEP 409 implementation. See #14133.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index a11283e..b994862 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -42,6 +42,7 @@
     /* 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) {
@@ -266,24 +267,7 @@
     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
@@ -291,8 +275,18 @@
     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;
 }
 
 
@@ -333,6 +327,7 @@
 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);
 }
 
@@ -352,6 +347,12 @@
 }
 
 
+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*/
@@ -382,7 +383,7 @@
     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 */