diff options
-rw-r--r-- | Doc/c-api/exceptions.rst | 46 | ||||
-rw-r--r-- | Lib/test/test_exceptions.py | 6 | ||||
-rw-r--r-- | Misc/NEWS | 6 | ||||
-rw-r--r-- | Objects/exceptions.c | 66 |
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) @@ -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 */ |