Index: Objects/exceptions.c =================================================================== --- Objects/exceptions.c (Revision 56829) +++ Objects/exceptions.c (Arbeitskopie) @@ -38,18 +38,31 @@ /* the dict is created on the fly in PyObject_GenericSetAttr */ self->message = self->dict = NULL; - self->args = PyTuple_New(0); - if (!self->args) { + if (!args) { + /* MemoryError instantiation */ + args = PyTuple_New(0); + if (!args) { + Py_DECREF(self); + return NULL; + } + } else { + Py_INCREF(args); + } + + self->args = args; + + /* Since the args can be overwritten in __init__, we have to store + the original args somewhere for pickling. */ + if (PyObject_SetAttrString((PyObject *)self, "__newargs__", args) < 0) { Py_DECREF(self); return NULL; } - + self->message = PyString_FromString(""); if (!self->message) { Py_DECREF(self); return NULL; } - return (PyObject *)self; } @@ -147,10 +160,23 @@ static PyObject * BaseException_reduce(PyBaseExceptionObject *self) { + PyObject *result; + PyObject *newargs = PyObject_GetAttrString((PyObject *)self, "__newargs__"); + if (!newargs) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_SetString(PyExc_AttributeError, + "To pickle exceptions via BaseException.__reduce__, " + "you need to set the __newargs__ attribute in your " + "custom __new__ method."); + } + return NULL; + } if (self->args && self->dict) - return PyTuple_Pack(3, Py_Type(self), self->args, self->dict); + result = PyTuple_Pack(3, Py_Type(self), newargs, self->dict); else - return PyTuple_Pack(2, Py_Type(self), self->args); + result = PyTuple_Pack(2, Py_Type(self), newargs); + Py_DECREF(newargs); + return result; } /* Index: Lib/test/test_exceptions.py =================================================================== --- Lib/test/test_exceptions.py (Revision 56829) +++ Lib/test/test_exceptions.py (Arbeitskopie) @@ -9,6 +9,16 @@ guard_warnings_filter) from test.test_pep352 import ignore_message_warning +class NaiveException(Exception): + def __init__(self, x): + self.x = x + +class SomewhatNaiveException(Exception): + def __init__(self, x): + self.x = x + Exception.__init__(self) + + # XXX This is not really enough, each *operation* should be tested! class ExceptionTests(unittest.TestCase): @@ -263,6 +273,10 @@ {'message' : '', 'args' : (u'\u3042', 0, 1, 'ouch'), 'object' : u'\u3042', 'reason' : 'ouch', 'start' : 0, 'end' : 1}), + (NaiveException, ('foo',), + {'message': '', 'args': ('foo',), 'x': 'foo'}), + (SomewhatNaiveException, ('foo',), + {'message': '', 'args': (), 'x': 'foo'}), ] try: exceptionList.append( @@ -283,7 +297,8 @@ if type(e) is not exc: raise # Verify module name - self.assertEquals(type(e).__module__, 'exceptions') + if not type(e).__name__.endswith('NaiveException'): + self.assertEquals(type(e).__module__, 'exceptions') # Verify no ref leaks in Exc_str() s = str(e) for checkArgName in expected: