Skip to content

Commit af685f9

Browse files
bpo-29998: Pickling and copying ImportError now preserves name and path (python#1010) (python#1042)
attributes. (cherry picked from commit b785396)
1 parent c26b19d commit af685f9

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

Lib/test/test_exceptions.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Python test set -- part 5, built-in exceptions
22

3+
import copy
34
import os
45
import sys
56
import unittest
@@ -1119,6 +1120,25 @@ def test_non_str_argument(self):
11191120
exc = ImportError(arg)
11201121
self.assertEqual(str(arg), str(exc))
11211122

1123+
def test_copy_pickle(self):
1124+
for kwargs in (dict(),
1125+
dict(name='somename'),
1126+
dict(path='somepath'),
1127+
dict(name='somename', path='somepath')):
1128+
orig = ImportError('test', **kwargs)
1129+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1130+
exc = pickle.loads(pickle.dumps(orig, proto))
1131+
self.assertEqual(exc.args, ('test',))
1132+
self.assertEqual(exc.msg, 'test')
1133+
self.assertEqual(exc.name, orig.name)
1134+
self.assertEqual(exc.path, orig.path)
1135+
for c in copy.copy, copy.deepcopy:
1136+
exc = c(orig)
1137+
self.assertEqual(exc.args, ('test',))
1138+
self.assertEqual(exc.msg, 'test')
1139+
self.assertEqual(exc.name, orig.name)
1140+
self.assertEqual(exc.path, orig.path)
1141+
11221142

11231143
if __name__ == '__main__':
11241144
unittest.main()

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ Core and Builtins
3232
Library
3333
-------
3434

35+
- bpo-29998: Pickling and copying ImportError now preserves name and path
36+
attributes.
37+
3538
- bpo-29953: Fixed memory leaks in the replace() method of datetime and time
3639
objects when pass out of bound fold argument.
3740

Objects/exceptions.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,53 @@ ImportError_str(PyImportErrorObject *self)
686686
}
687687
}
688688

689+
static PyObject *
690+
ImportError_getstate(PyImportErrorObject *self)
691+
{
692+
PyObject *dict = ((PyBaseExceptionObject *)self)->dict;
693+
if (self->name || self->path) {
694+
_Py_IDENTIFIER(name);
695+
_Py_IDENTIFIER(path);
696+
dict = dict ? PyDict_Copy(dict) : PyDict_New();
697+
if (dict == NULL)
698+
return NULL;
699+
if (self->name && _PyDict_SetItemId(dict, &PyId_name, self->name) < 0) {
700+
Py_DECREF(dict);
701+
return NULL;
702+
}
703+
if (self->path && _PyDict_SetItemId(dict, &PyId_path, self->path) < 0) {
704+
Py_DECREF(dict);
705+
return NULL;
706+
}
707+
return dict;
708+
}
709+
else if (dict) {
710+
Py_INCREF(dict);
711+
return dict;
712+
}
713+
else {
714+
Py_RETURN_NONE;
715+
}
716+
}
717+
718+
/* Pickling support */
719+
static PyObject *
720+
ImportError_reduce(PyImportErrorObject *self)
721+
{
722+
PyObject *res;
723+
PyObject *args;
724+
PyObject *state = ImportError_getstate(self);
725+
if (state == NULL)
726+
return NULL;
727+
args = ((PyBaseExceptionObject *)self)->args;
728+
if (state == Py_None)
729+
res = PyTuple_Pack(2, Py_TYPE(self), args);
730+
else
731+
res = PyTuple_Pack(3, Py_TYPE(self), args, state);
732+
Py_DECREF(state);
733+
return res;
734+
}
735+
689736
static PyMemberDef ImportError_members[] = {
690737
{"msg", T_OBJECT, offsetof(PyImportErrorObject, msg), 0,
691738
PyDoc_STR("exception message")},
@@ -697,6 +744,7 @@ static PyMemberDef ImportError_members[] = {
697744
};
698745

699746
static PyMethodDef ImportError_methods[] = {
747+
{"__reduce__", (PyCFunction)ImportError_reduce, METH_NOARGS},
700748
{NULL}
701749
};
702750

0 commit comments

Comments
 (0)