Index: Include/pyerrors.h =================================================================== --- Include/pyerrors.h (revision 74825) +++ Include/pyerrors.h (working copy) @@ -11,6 +11,12 @@ PyObject *dict; PyObject *args; PyObject *message; + /* + * A flag which tracks whether the the message attribute has been + * explicitly set, rather than just initialized in the deprecated way by + * __init__. + */ + int message_set; } PyBaseExceptionObject; typedef struct { Index: Objects/exceptions.c =================================================================== --- Objects/exceptions.c (revision 74825) +++ Objects/exceptions.c (working copy) @@ -37,6 +37,7 @@ return NULL; /* the dict is created on the fly in PyObject_GenericSetAttr */ self->message = self->dict = NULL; + self->message_set = 0; self->args = PyTuple_New(0); if (!self->args) { @@ -301,12 +302,14 @@ static PyObject * BaseException_get_message(PyBaseExceptionObject *self) { - int ret; - ret = PyErr_WarnEx(PyExc_DeprecationWarning, - "BaseException.message has been deprecated as " - "of Python 2.6", 1); - if (ret < 0) - return NULL; + if (!self->message_set) { + int ret = 0; + ret = PyErr_WarnEx(PyExc_DeprecationWarning, + "BaseException.message has been deprecated as " + "of Python 2.6", 1); + if (ret < 0) + return NULL; + } Py_INCREF(self->message); return self->message; @@ -315,15 +318,10 @@ static int BaseException_set_message(PyBaseExceptionObject *self, PyObject *val) { - int ret; - ret = PyErr_WarnEx(PyExc_DeprecationWarning, - "BaseException.message has been deprecated as " - "of Python 2.6", 1); - if (ret < 0) - return -1; Py_INCREF(val); Py_DECREF(self->message); self->message = val; + self->message_set = 1; return 0; } Index: Lib/test/test_exceptions.py =================================================================== --- Lib/test/test_exceptions.py (revision 74825) +++ Lib/test/test_exceptions.py (working copy) @@ -303,6 +303,30 @@ 'pickled "%r", attribute "%s"' % (e, checkArgName)) + + def testDeprecatedMessageAttribute(self): + # Accessing BaseException.message and relying on its value set by + # BaseException.__init__ triggers a deprecation warning. + exc = BaseException("foo") + with warnings.catch_warnings(record=True) as w: + self.assertEquals(exc.message, "foo") + self.assertEquals(len(w), 1) + self.assertEquals(w[0].category, DeprecationWarning) + self.assertEquals( + str(w[0].message), + "BaseException.message has been deprecated as of Python 2.6") + + + def testRegularMessageAttribute(self): + # Accessing BaseException.message after explicitly setting a value + # for it does not trigger a deprecation warning. + exc = BaseException("foo") + exc.message = "bar" + with warnings.catch_warnings(record=True) as w: + self.assertEquals(exc.message, "bar") + self.assertEquals(len(w), 0) + + def testSlicing(self): # Test that you can slice an exception directly instead of requiring # going through the 'args' attribute.