summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2017-04-08 08:25:47 (GMT)
committerGitHub <noreply@github.com>2017-04-08 08:25:47 (GMT)
commitaf685f9050416da8050e0ec11a8dff9afd4130e7 (patch)
tree6cc7ad8a88bd7458913a0b1501601b230c62f513
parentc26b19d5c7aba51b50a4d7fb5f8291036cb9da24 (diff)
downloadcpython-af685f9050416da8050e0ec11a8dff9afd4130e7.zip
cpython-af685f9050416da8050e0ec11a8dff9afd4130e7.tar.gz
cpython-af685f9050416da8050e0ec11a8dff9afd4130e7.tar.bz2
bpo-29998: Pickling and copying ImportError now preserves name and path (#1010) (#1042)
attributes. (cherry picked from commit b785396ab451b0c9d6ae9ee5a9e56c810209a6cb)
-rw-r--r--Lib/test/test_exceptions.py20
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/exceptions.c48
3 files changed, 71 insertions, 0 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 4837922..960fc0f 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -1,5 +1,6 @@
# Python test set -- part 5, built-in exceptions
+import copy
import os
import sys
import unittest
@@ -1119,6 +1120,25 @@ class ImportErrorTests(unittest.TestCase):
exc = ImportError(arg)
self.assertEqual(str(arg), str(exc))
+ def test_copy_pickle(self):
+ for kwargs in (dict(),
+ dict(name='somename'),
+ dict(path='somepath'),
+ dict(name='somename', path='somepath')):
+ orig = ImportError('test', **kwargs)
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ exc = pickle.loads(pickle.dumps(orig, proto))
+ self.assertEqual(exc.args, ('test',))
+ self.assertEqual(exc.msg, 'test')
+ self.assertEqual(exc.name, orig.name)
+ self.assertEqual(exc.path, orig.path)
+ for c in copy.copy, copy.deepcopy:
+ exc = c(orig)
+ self.assertEqual(exc.args, ('test',))
+ self.assertEqual(exc.msg, 'test')
+ self.assertEqual(exc.name, orig.name)
+ self.assertEqual(exc.path, orig.path)
+
if __name__ == '__main__':
unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
index fa20ea1..83f9e7a 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -32,6 +32,9 @@ Core and Builtins
Library
-------
+- bpo-29998: Pickling and copying ImportError now preserves name and path
+ attributes.
+
- bpo-29953: Fixed memory leaks in the replace() method of datetime and time
objects when pass out of bound fold argument.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index f63f06a..d158b97 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -686,6 +686,53 @@ ImportError_str(PyImportErrorObject *self)
}
}
+static PyObject *
+ImportError_getstate(PyImportErrorObject *self)
+{
+ PyObject *dict = ((PyBaseExceptionObject *)self)->dict;
+ if (self->name || self->path) {
+ _Py_IDENTIFIER(name);
+ _Py_IDENTIFIER(path);
+ dict = dict ? PyDict_Copy(dict) : PyDict_New();
+ if (dict == NULL)
+ return NULL;
+ if (self->name && _PyDict_SetItemId(dict, &PyId_name, self->name) < 0) {
+ Py_DECREF(dict);
+ return NULL;
+ }
+ if (self->path && _PyDict_SetItemId(dict, &PyId_path, self->path) < 0) {
+ Py_DECREF(dict);
+ return NULL;
+ }
+ return dict;
+ }
+ else if (dict) {
+ Py_INCREF(dict);
+ return dict;
+ }
+ else {
+ Py_RETURN_NONE;
+ }
+}
+
+/* Pickling support */
+static PyObject *
+ImportError_reduce(PyImportErrorObject *self)
+{
+ PyObject *res;
+ PyObject *args;
+ PyObject *state = ImportError_getstate(self);
+ if (state == NULL)
+ return NULL;
+ args = ((PyBaseExceptionObject *)self)->args;
+ if (state == Py_None)
+ res = PyTuple_Pack(2, Py_TYPE(self), args);
+ else
+ res = PyTuple_Pack(3, Py_TYPE(self), args, state);
+ Py_DECREF(state);
+ return res;
+}
+
static PyMemberDef ImportError_members[] = {
{"msg", T_OBJECT, offsetof(PyImportErrorObject, msg), 0,
PyDoc_STR("exception message")},
@@ -697,6 +744,7 @@ static PyMemberDef ImportError_members[] = {
};
static PyMethodDef ImportError_methods[] = {
+ {"__reduce__", (PyCFunction)ImportError_reduce, METH_NOARGS},
{NULL}
};