summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/exceptions.rst46
-rw-r--r--Lib/test/test_exceptions.py6
-rw-r--r--Misc/NEWS6
-rw-r--r--Objects/exceptions.c66
4 files changed, 114 insertions, 10 deletions
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index f650cfd..819e22e 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -400,6 +400,52 @@ in various ways. There is a separate error indicator for each thread.
the warning message.
+Exception Objects
+=================
+
+.. cfunction:: PyObject* PyException_GetTraceback(PyObject *ex)
+
+ Return the traceback associated with the exception as a new reference, as
+ accessible from Python through :attr:`__traceback__`. If there is no
+ traceback associated, this returns *NULL*.
+
+
+.. cfunction:: int PyException_SetTraceback(PyObject *ex, PyObject *tb)
+
+ Set the traceback associated with the exception to *tb*. Use ``Py_None`` to
+ clear it.
+
+
+.. cfunction:: PyObject* PyException_GetContext(PyObject *ex)
+
+ Return the context (another exception instance during whose handling *ex* was
+ raised) associated with the exception as a new reference, as accessible from
+ Python through :attr:`__context__`. If there is no context associated, this
+ returns *NULL*.
+
+
+.. cfunction:: void PyException_SetContext(PyObject *ex, PyObject *ctx)
+
+ Set the context associated with the exception to *ctx*. Use *NULL* to clear
+ it. There is no type check to make sure that *ctx* is an exception instance.
+ This steals a reference to *ctx*.
+
+
+.. cfunction:: PyObject* PyException_GetCause(PyObject *ex)
+
+ Return the cause (another exception instance 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*.
+
+
+.. cfunction:: 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 an exception instance.
+ This steals a reference to *ctx*.
+
+
.. _standardexceptions:
Standard Exceptions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index b671cbc..8bb2079 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -341,6 +341,12 @@ class ExceptionTests(unittest.TestCase):
else:
self.fail("No exception raised")
+ def testInvalidAttrs(self):
+ self.assertRaises(TypeError, setattr, Exception(), '__cause__', 1)
+ self.assertRaises(TypeError, delattr, Exception(), '__cause__')
+ self.assertRaises(TypeError, setattr, Exception(), '__context__', 1)
+ self.assertRaises(TypeError, delattr, Exception(), '__context__')
+
def testNoneClearsTracebackAttr(self):
try:
raise IndexError(4)
diff --git a/Misc/NEWS b/Misc/NEWS
index 894aa71..911e944 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1,4 +1,5 @@
-+++++++++++ Python News
++++++++++++
+Python News
+++++++++++
(editors: check NEWS.help for information about editing NEWS using ReST.)
@@ -11,6 +12,9 @@ What's New in Python 3.1 alpha 2?
Core and Builtins
-----------------
+- Fix a segfault when running test_exceptions with coverage, caused by
+ insufficient checks in accessors of Exception.__context__.
+
- Issue #5604: non-ASCII characters in module name passed to
imp.find_module() were converted to UTF-8 while the path is
converted to the default filesystem encoding, causing nonsense.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index 418bffb..9258ace 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -250,11 +250,67 @@ BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb)
return 0;
}
+static PyObject *
+BaseException_get_context(PyObject *self) {
+ PyObject *res = PyException_GetContext(self);
+ if (res) return res; /* new reference already returned above */
+ Py_RETURN_NONE;
+}
+
+static int
+BaseException_set_context(PyObject *self, PyObject *arg) {
+ if (arg == NULL) {
+ PyErr_SetString(PyExc_TypeError, "__context__ may not be deleted");
+ return -1;
+ } else if (arg == Py_None) {
+ arg = NULL;
+ } else if (!PyExceptionInstance_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError, "exception context must be None "
+ "or derive from BaseException");
+ return -1;
+ } else {
+ /* PyException_SetContext steals this reference */
+ Py_INCREF(arg);
+ }
+ PyException_SetContext(self, arg);
+ return 0;
+}
+
+static PyObject *
+BaseException_get_cause(PyObject *self) {
+ PyObject *res = PyException_GetCause(self);
+ if (res) return res; /* new reference already returned above */
+ 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);
+ }
+ PyException_SetCause(self, arg);
+ return 0;
+}
+
static PyGetSetDef BaseException_getset[] = {
{"__dict__", (getter)BaseException_get_dict, (setter)BaseException_set_dict},
{"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
{"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb},
+ {"__context__", (getter)BaseException_get_context,
+ (setter)BaseException_set_context, PyDoc_STR("exception context")},
+ {"__cause__", (getter)BaseException_get_cause,
+ (setter)BaseException_set_cause, PyDoc_STR("exception cause")},
{NULL},
};
@@ -303,14 +359,6 @@ PyException_SetContext(PyObject *self, PyObject *context) {
}
-static PyMemberDef BaseException_members[] = {
- {"__context__", T_OBJECT, offsetof(PyBaseExceptionObject, context), 0,
- PyDoc_STR("exception context")},
- {"__cause__", T_OBJECT, offsetof(PyBaseExceptionObject, cause), 0,
- PyDoc_STR("exception cause")},
- {NULL} /* Sentinel */
-};
-
static PyTypeObject _PyExc_BaseException = {
PyVarObject_HEAD_INIT(NULL, 0)
"BaseException", /*tp_name*/
@@ -341,7 +389,7 @@ static PyTypeObject _PyExc_BaseException = {
0, /* tp_iter */
0, /* tp_iternext */
BaseException_methods, /* tp_methods */
- BaseException_members, /* tp_members */
+ 0, /* tp_members */
BaseException_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */