Index: Objects/exceptions.c =================================================================== --- Objects/exceptions.c (revision 56094) +++ Objects/exceptions.c (working copy) @@ -145,14 +145,69 @@ /* Pickling support */ static PyObject * -BaseException_reduce(PyBaseExceptionObject *self) +common_exception_reduce_ex(PyBaseExceptionObject *self, PyObject **dict_out) { - if (self->args && self->dict) - return PyTuple_Pack(3, self->ob_type, self->args, self->dict); - else - return PyTuple_Pack(2, self->ob_type, self->args); + PyObject *result = NULL; + PyObject *constructor = NULL; + PyObject *constructor_args = NULL; + PyObject *state = NULL; + PyObject *copy_reg = NULL; + + copy_reg = PyImport_ImportModule("copy_reg"); + if (copy_reg == NULL) { + goto end; + } + + /* Unfortunately BaseException.__new__ is not pickleable. */ + constructor = PyObject_GetAttrString(copy_reg, "_exception_constructor"); + if (constructor == NULL) { + goto end; + } + + constructor_args = PyTuple_Pack(1, self->ob_type); + if (constructor_args == NULL) { + goto end; + } + + state = PyDict_New(); + if (state == NULL) { + goto end; + } + if (self->dict) { + if (PyDict_Update(state, self->dict)) { + goto end; + } + } + if (PyDict_SetItemString(state, "args", self->args)) { + goto end; + } + if (PyDict_SetItemString(state, "message", self->message)) { + goto end; + } + *dict_out = state; + + result = PyTuple_Pack(3, constructor, constructor_args, state); +end: + Py_XDECREF(constructor); + Py_XDECREF(constructor_args); + Py_XDECREF(state); + Py_XDECREF(copy_reg); + return result; } +static PyObject * +BaseException_reduce_ex(PyBaseExceptionObject *self, PyObject *proto) +{ + PyObject *result; + PyObject *state; + + result = common_exception_reduce_ex(self, &state); + if (result == NULL) { + return NULL; + } + return result; +} + /* * Needed for backward compatibility, since exceptions used to store * all their attributes in the __dict__. Code is taken from cPickle's @@ -179,7 +234,7 @@ static PyMethodDef BaseException_methods[] = { - {"__reduce__", (PyCFunction)BaseException_reduce, METH_NOARGS }, + {"__reduce_ex__", (PyCFunction)BaseException_reduce_ex, METH_O }, {"__setstate__", (PyCFunction)BaseException_setstate, METH_O }, {NULL, NULL, 0, NULL}, }; @@ -700,41 +755,36 @@ static PyObject * -EnvironmentError_reduce(PyEnvironmentErrorObject *self) +EnvironmentError_reduce_ex(PyEnvironmentErrorObject *self, PyObject *proto) { - PyObject *args = self->args; - PyObject *res = NULL, *tmp; + PyObject *result; + PyObject *state; + + result = common_exception_reduce_ex((PyBaseExceptionObject *)self, &state); + if (result == NULL) { + return NULL; + } + if (PyDict_SetItemString(state, "errno", self->myerrno)) { + Py_DECREF(result); + return NULL; + } + if (PyDict_SetItemString(state, "strerror", self->strerror)) { + Py_DECREF(result); + return NULL; + } + if (self->filename) { + if (PyDict_SetItemString(state, "filename", self->filename)) { + Py_DECREF(result); + return NULL; + } + } - /* self->args is only the first two real arguments if there was a - * file name given to EnvironmentError. */ - if (PyTuple_GET_SIZE(args) == 2 && self->filename) { - args = PyTuple_New(3); - if (!args) return NULL; - - tmp = PyTuple_GET_ITEM(self->args, 0); - Py_INCREF(tmp); - PyTuple_SET_ITEM(args, 0, tmp); - - tmp = PyTuple_GET_ITEM(self->args, 1); - Py_INCREF(tmp); - PyTuple_SET_ITEM(args, 1, tmp); - - Py_INCREF(self->filename); - PyTuple_SET_ITEM(args, 2, self->filename); - } else - Py_INCREF(args); - - if (self->dict) - res = PyTuple_Pack(3, self->ob_type, args, self->dict); - else - res = PyTuple_Pack(2, self->ob_type, args); - Py_DECREF(args); - return res; + return result; } static PyMethodDef EnvironmentError_methods[] = { - {"__reduce__", (PyCFunction)EnvironmentError_reduce, METH_NOARGS}, + {"__reduce_ex__", (PyCFunction)EnvironmentError_reduce_ex, METH_O}, {NULL} }; Index: Lib/copy_reg.py =================================================================== --- Lib/copy_reg.py (revision 56094) +++ Lib/copy_reg.py (working copy) @@ -41,6 +41,10 @@ pickle(complex, pickle_complex, complex) +# This is here because pickle can't pickle BaseException.__new__ directly. +def _exception_constructor(cls): + return BaseException.__new__(cls) + # Support for pickling new-style objects def _reconstructor(cls, base, state):