Skip to content

Commit d92f040

Browse files
committed
Issue #5180: Fixed a bug that prevented loading 2.x pickles in 3.x
python when they contain instances of old-style classes.
1 parent bbda0c5 commit d92f040

4 files changed

Lines changed: 91 additions & 40 deletions

File tree

‎Lib/pickle.py‎

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,19 +1034,15 @@ def load_dict(self):
10341034
def _instantiate(self, klass, k):
10351035
args = tuple(self.stack[k+1:])
10361036
del self.stack[k:]
1037-
instantiated = False
1038-
if (not args and
1039-
isinstance(klass, type) and
1040-
not hasattr(klass, "__getinitargs__")):
1041-
value = _EmptyClass()
1042-
value.__class__ = klass
1043-
instantiated = True
1044-
if not instantiated:
1037+
if (args or not isinstance(klass, type) or
1038+
hasattr(klass, "__getinitargs__")):
10451039
try:
10461040
value = klass(*args)
10471041
except TypeError as err:
10481042
raise TypeError("in constructor for %s: %s" %
10491043
(klass.__name__, str(err)), sys.exc_info()[2])
1044+
else:
1045+
value = klass.__new__(klass)
10501046
self.append(value)
10511047

10521048
def load_inst(self):
@@ -1239,14 +1235,7 @@ def load_stop(self):
12391235
raise _Stop(value)
12401236
dispatch[STOP[0]] = load_stop
12411237

1242-
# Helper class for load_inst/load_obj
1243-
1244-
class _EmptyClass:
1245-
pass
1246-
1247-
# Encode/decode longs in linear time.
1248-
1249-
import binascii as _binascii
1238+
# Encode/decode longs.
12501239

12511240
def encode_long(x):
12521241
r"""Encode a long to a two's complement little-endian binary string.

‎Lib/test/pickletester.py‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,21 @@ class C:
6565
def __eq__(self, other):
6666
return self.__dict__ == other.__dict__
6767

68+
class D(C):
69+
def __init__(self, arg):
70+
pass
71+
72+
class E(C):
73+
def __getinitargs__(self):
74+
return ()
75+
6876
import __main__
6977
__main__.C = C
7078
C.__module__ = "__main__"
79+
__main__.D = D
80+
D.__module__ = "__main__"
81+
__main__.E = E
82+
E.__module__ = "__main__"
7183

7284
class myint(int):
7385
def __init__(self, x):
@@ -425,6 +437,63 @@ def test_load_from_data1(self):
425437
def test_load_from_data2(self):
426438
self.assertEqual(self._testdata, self.loads(DATA2))
427439

440+
def test_load_classic_instance(self):
441+
# See issue5180. Test loading 2.x pickles that
442+
# contain an instance of old style class.
443+
for X, args in [(C, ()), (D, ('x',)), (E, ())]:
444+
xname = X.__name__.encode('ascii')
445+
# Protocol 0 (text mode pickle):
446+
"""
447+
0: ( MARK
448+
1: i INST '__main__ X' (MARK at 0)
449+
15: p PUT 0
450+
18: ( MARK
451+
19: d DICT (MARK at 18)
452+
20: p PUT 1
453+
23: b BUILD
454+
24: . STOP
455+
"""
456+
pickle0 = (b"(i__main__\n"
457+
b"X\n"
458+
b"p0\n"
459+
b"(dp1\nb.").replace(b'X', xname)
460+
self.assertEqual(X(*args), self.loads(pickle0))
461+
462+
# Protocol 1 (binary mode pickle)
463+
"""
464+
0: ( MARK
465+
1: c GLOBAL '__main__ X'
466+
15: q BINPUT 0
467+
17: o OBJ (MARK at 0)
468+
18: q BINPUT 1
469+
20: } EMPTY_DICT
470+
21: q BINPUT 2
471+
23: b BUILD
472+
24: . STOP
473+
"""
474+
pickle1 = (b'(c__main__\n'
475+
b'X\n'
476+
b'q\x00oq\x01}q\x02b.').replace(b'X', xname)
477+
self.assertEqual(X(*args), self.loads(pickle1))
478+
479+
# Protocol 2 (pickle2 = b'\x80\x02' + pickle1)
480+
"""
481+
0: \x80 PROTO 2
482+
2: ( MARK
483+
3: c GLOBAL '__main__ X'
484+
17: q BINPUT 0
485+
19: o OBJ (MARK at 2)
486+
20: q BINPUT 1
487+
22: } EMPTY_DICT
488+
23: q BINPUT 2
489+
25: b BUILD
490+
26: . STOP
491+
"""
492+
pickle2 = (b'\x80\x02(c__main__\n'
493+
b'X\n'
494+
b'q\x00oq\x01}q\x02b.').replace(b'X', xname)
495+
self.assertEqual(X(*args), self.loads(pickle2))
496+
428497
# There are gratuitous differences between pickles produced by
429498
# pickle and cPickle, largely because cPickle starts PUT indices at
430499
# 1 and pickle starts them at 0. See XXX comment in cPickle's put2() --

‎Misc/NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,9 @@ Library
14291429
Extension Modules
14301430
-----------------
14311431

1432+
- Issue #5180: Fixed a bug that prevented loading 2.x pickles in 3.x
1433+
python when they contain instances of old-style classes.
1434+
14321435
- Issue #9165: Add new functions math.isfinite and cmath.isfinite, to
14331436
accompany existing isinf and isnan functions.
14341437

‎Modules/_pickle.c‎

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3466,29 +3466,19 @@ load_dict(UnpicklerObject *self)
34663466
static PyObject *
34673467
instantiate(PyObject *cls, PyObject *args)
34683468
{
3469-
PyObject *r = NULL;
3470-
3471-
/* XXX: The pickle.py module does not create instances this way when the
3472-
args tuple is empty. See Unpickler._instantiate(). */
3473-
if ((r = PyObject_CallObject(cls, args)))
3474-
return r;
3475-
3476-
/* XXX: Is this still nescessary? */
3477-
{
3478-
PyObject *tp, *v, *tb, *tmp_value;
3479-
3480-
PyErr_Fetch(&tp, &v, &tb);
3481-
tmp_value = v;
3482-
/* NULL occurs when there was a KeyboardInterrupt */
3483-
if (tmp_value == NULL)
3484-
tmp_value = Py_None;
3485-
if ((r = PyTuple_Pack(3, tmp_value, cls, args))) {
3486-
Py_XDECREF(v);
3487-
v = r;
3488-
}
3489-
PyErr_Restore(tp, v, tb);
3469+
PyObject *result = NULL;
3470+
/* Caller must assure args are a tuple. Normally, args come from
3471+
Pdata_poptuple which packs objects from the top of the stack
3472+
into a newly created tuple. */
3473+
assert(PyTuple_Check(args));
3474+
if (Py_SIZE(args) > 0 || !PyType_Check(cls) ||
3475+
PyObject_HasAttrString(cls, "__getinitargs__")) {
3476+
result = PyObject_CallObject(cls, args);
34903477
}
3491-
return NULL;
3478+
else {
3479+
result = PyObject_CallMethod(cls, "__new__", "O", cls);
3480+
}
3481+
return result;
34923482
}
34933483

34943484
static int
@@ -3547,7 +3537,7 @@ load_inst(UnpicklerObject *self)
35473537
if (len < 2)
35483538
return bad_readline();
35493539
class_name = PyUnicode_DecodeASCII(s, len - 1, "strict");
3550-
if (class_name == NULL) {
3540+
if (class_name != NULL) {
35513541
cls = find_class(self, module_name, class_name);
35523542
Py_DECREF(class_name);
35533543
}
@@ -4276,7 +4266,7 @@ load_reduce(UnpicklerObject *self)
42764266
return -1;
42774267
PDATA_POP(self->stack, callable);
42784268
if (callable) {
4279-
obj = instantiate(callable, argtup);
4269+
obj = PyObject_CallObject(callable, argtup);
42804270
Py_DECREF(callable);
42814271
}
42824272
Py_DECREF(argtup);

0 commit comments

Comments
 (0)