Beefed up the tests by putting in more "for proto in protocols:" outer

loops.  Renamed DATA and BINDATA to DATA0 and DATA1.  Included
disassemblies, but noted why we can't test them.  Added XXX comment to
cPickle about a mysterious comment, where pickle and cPickle diverge
in how they number PUT indices.
This commit is contained in:
Tim Peters 2003-02-02 17:26:40 +00:00
parent 6c54ef6884
commit 70b02d79f9
2 changed files with 234 additions and 84 deletions

View File

@ -35,11 +35,12 @@ class metaclass(type):
class use_metaclass(object): class use_metaclass(object):
__metaclass__ = metaclass __metaclass__ = metaclass
# DATA and BINDATA are the protocol 0 and protocol 1 pickles of the object # DATA0 .. DATA2 are the pickles we expect under the various protocols, for
# returned by create_data(). # the object returned by create_data().
# XXX DATA2 doesn't exist yet, as it's not fully implemented in cPickle.
# break into multiple strings to avoid confusing font-lock-mode # break into multiple strings to avoid confusing font-lock-mode
DATA = """(lp1 DATA0 = """(lp1
I0 I0
aL1L aL1L
aF2 aF2
@ -84,14 +85,145 @@ aI5
a. a.
""" """
BINDATA = ']q\x01(K\x00L1L\nG@\x00\x00\x00\x00\x00\x00\x00' + \ # Disassembly of DATA0.
'c__builtin__\ncomplex\nq\x02(G@\x08\x00\x00\x00\x00\x00' + \ DATA0_DIS = """\
'\x00G\x00\x00\x00\x00\x00\x00\x00\x00tRq\x03K\x01J\xff\xff' + \ 0: ( MARK
'\xff\xffK\xffJ\x01\xff\xff\xffJ\x00\xff\xff\xffM\xff\xff' + \ 1: l LIST (MARK at 0)
'J\x01\x00\xff\xffJ\x00\x00\xff\xffJ\xff\xff\xff\x7fJ\x01\x00' + \ 2: p PUT 1
'\x00\x80J\x00\x00\x00\x80(U\x03abcq\x04h\x04(c__main__\n' + \ 5: I INT 0
'C\nq\x05oq\x06}q\x07(U\x03fooq\x08K\x01U\x03barq\tK\x02ubh' + \ 8: a APPEND
'\x06tq\nh\nK\x05e.' 9: L LONG 1L
13: a APPEND
14: F FLOAT 2.0
17: a APPEND
18: c GLOBAL '__builtin__ complex'
39: p PUT 2
42: ( MARK
43: F FLOAT 3.0
46: F FLOAT 0.0
49: t TUPLE (MARK at 42)
50: R REDUCE
51: p PUT 3
54: a APPEND
55: I INT 1
58: a APPEND
59: I INT -1
63: a APPEND
64: I INT 255
69: a APPEND
70: I INT -255
76: a APPEND
77: I INT -256
83: a APPEND
84: I INT 65535
91: a APPEND
92: I INT -65535
100: a APPEND
101: I INT -65536
109: a APPEND
110: I INT 2147483647
122: a APPEND
123: I INT -2147483647
136: a APPEND
137: I INT -2147483648
150: a APPEND
151: ( MARK
152: S STRING 'abc'
159: p PUT 4
162: g GET 4
165: ( MARK
166: i INST '__main__ C' (MARK at 165)
178: p PUT 5
181: ( MARK
182: d DICT (MARK at 181)
183: p PUT 6
186: S STRING 'foo'
193: p PUT 7
196: I INT 1
199: s SETITEM
200: S STRING 'bar'
207: p PUT 8
210: I INT 2
213: s SETITEM
214: b BUILD
215: g GET 5
218: t TUPLE (MARK at 151)
219: p PUT 9
222: a APPEND
223: g GET 9
226: a APPEND
227: I INT 5
230: a APPEND
231: . STOP
highest protocol among opcodes = 0
"""
DATA1 = (']q\x01(K\x00L1L\nG@\x00\x00\x00\x00\x00\x00\x00'
'c__builtin__\ncomplex\nq\x02(G@\x08\x00\x00\x00\x00\x00'
'\x00G\x00\x00\x00\x00\x00\x00\x00\x00tRq\x03K\x01J\xff\xff'
'\xff\xffK\xffJ\x01\xff\xff\xffJ\x00\xff\xff\xffM\xff\xff'
'J\x01\x00\xff\xffJ\x00\x00\xff\xffJ\xff\xff\xff\x7fJ\x01\x00'
'\x00\x80J\x00\x00\x00\x80(U\x03abcq\x04h\x04(c__main__\n'
'C\nq\x05oq\x06}q\x07(U\x03fooq\x08K\x01U\x03barq\tK\x02ubh'
'\x06tq\nh\nK\x05e.'
)
# Disassembly of DATA1.
DATA1_DIS = """\
0: ] EMPTY_LIST
1: q BINPUT 1
3: ( MARK
4: K BININT1 0
6: L LONG 1L
10: G BINFLOAT 2.0
19: c GLOBAL '__builtin__ complex'
40: q BINPUT 2
42: ( MARK
43: G BINFLOAT 3.0
52: G BINFLOAT 0.0
61: t TUPLE (MARK at 42)
62: R REDUCE
63: q BINPUT 3
65: K BININT1 1
67: J BININT -1
72: K BININT1 255
74: J BININT -255
79: J BININT -256
84: M BININT2 65535
87: J BININT -65535
92: J BININT -65536
97: J BININT 2147483647
102: J BININT -2147483647
107: J BININT -2147483648
112: ( MARK
113: U SHORT_BINSTRING 'abc'
118: q BINPUT 4
120: h BINGET 4
122: ( MARK
123: c GLOBAL '__main__ C'
135: q BINPUT 5
137: o OBJ (MARK at 122)
138: q BINPUT 6
140: } EMPTY_DICT
141: q BINPUT 7
143: ( MARK
144: U SHORT_BINSTRING 'foo'
149: q BINPUT 8
151: K BININT1 1
153: U SHORT_BINSTRING 'bar'
158: q BINPUT 9
160: K BININT1 2
162: u SETITEMS (MARK at 143)
163: b BUILD
164: h BINGET 6
166: t TUPLE (MARK at 112)
167: q BINPUT 10
169: h BINGET 10
171: K BININT1 5
173: e APPENDS (MARK at 3)
174: . STOP
highest protocol among opcodes = 1
"""
def create_data(): def create_data():
c = C() c = C()
@ -114,74 +246,92 @@ def create_data():
return x return x
class AbstractPickleTests(unittest.TestCase): class AbstractPickleTests(unittest.TestCase):
# Subclass must define self.dumps, self.loads, self.error.
_testdata = create_data() _testdata = create_data()
def setUp(self): def setUp(self):
# subclass must define self.dumps, self.loads, self.error
pass pass
def test_misc(self): def test_misc(self):
# test various datatypes not tested by testdata # test various datatypes not tested by testdata
x = myint(4) for proto in protocols:
s = self.dumps(x) x = myint(4)
y = self.loads(s) s = self.dumps(x, proto)
self.assertEqual(x, y) y = self.loads(s)
self.assertEqual(x, y)
x = (1, ()) x = (1, ())
s = self.dumps(x) s = self.dumps(x, proto)
y = self.loads(s) y = self.loads(s)
self.assertEqual(x, y) self.assertEqual(x, y)
x = initarg(1, x) x = initarg(1, x)
s = self.dumps(x) s = self.dumps(x, proto)
y = self.loads(s) y = self.loads(s)
self.assertEqual(x, y) self.assertEqual(x, y)
# XXX test __reduce__ protocol? # XXX test __reduce__ protocol?
def test_identity(self): def test_roundtrip_equality(self):
s = self.dumps(self._testdata) expected = self._testdata
x = self.loads(s) for proto in protocols:
self.assertEqual(x, self._testdata) s = self.dumps(expected, proto)
got = self.loads(s)
self.assertEqual(expected, got)
def test_constant(self): def test_load_from_canned_string(self):
x = self.loads(DATA) expected = self._testdata
self.assertEqual(x, self._testdata) for canned in DATA0, DATA1:
x = self.loads(BINDATA) got = self.loads(canned)
self.assertEqual(x, self._testdata) self.assertEqual(expected, got)
def test_binary(self): # There are gratuitous differences between pickles produced by
s = self.dumps(self._testdata, 1) # pickle and cPickle, largely because cPickle starts PUT indices at
x = self.loads(s) # 1 and pickle starts them at 0. See XXX comment in cPickle's put2() --
self.assertEqual(x, self._testdata) # there's a comment with an exclamation point there whose meaning
# is a mystery. cPickle also suppresses PUT for objects with a refcount
# of 1.
def dont_test_disassembly(self):
from cStringIO import StringIO
from pickletools import dis
for proto, expected in (0, DATA0_DIS), (1, DATA1_DIS):
s = self.dumps(self._testdata, proto)
filelike = StringIO()
dis(s, out=filelike)
got = filelike.getvalue()
self.assertEqual(expected, got)
def test_recursive_list(self): def test_recursive_list(self):
l = [] l = []
l.append(l) l.append(l)
s = self.dumps(l) for proto in protocols:
x = self.loads(s) s = self.dumps(l, proto)
self.assertEqual(x, l) x = self.loads(s)
self.assertEqual(x, x[0]) self.assertEqual(x, l)
self.assertEqual(id(x), id(x[0])) self.assertEqual(x, x[0])
self.assertEqual(id(x), id(x[0]))
def test_recursive_dict(self): def test_recursive_dict(self):
d = {} d = {}
d[1] = d d[1] = d
s = self.dumps(d) for proto in protocols:
x = self.loads(s) s = self.dumps(d, proto)
self.assertEqual(x, d) x = self.loads(s)
self.assertEqual(x[1], x) self.assertEqual(x, d)
self.assertEqual(id(x[1]), id(x)) self.assertEqual(x[1], x)
self.assertEqual(id(x[1]), id(x))
def test_recursive_inst(self): def test_recursive_inst(self):
i = C() i = C()
i.attr = i i.attr = i
s = self.dumps(i) for proto in protocols:
x = self.loads(s) s = self.dumps(i, 2)
self.assertEqual(x, i) x = self.loads(s)
self.assertEqual(x.attr, x) self.assertEqual(x, i)
self.assertEqual(id(x.attr), id(x)) self.assertEqual(x.attr, x)
self.assertEqual(id(x.attr), id(x))
def test_recursive_multi(self): def test_recursive_multi(self):
l = [] l = []
@ -189,14 +339,15 @@ class AbstractPickleTests(unittest.TestCase):
i = C() i = C()
i.attr = d i.attr = d
l.append(i) l.append(i)
s = self.dumps(l) for proto in protocols:
x = self.loads(s) s = self.dumps(l, proto)
self.assertEqual(x, l) x = self.loads(s)
self.assertEqual(x[0], i) self.assertEqual(x, l)
self.assertEqual(x[0].attr, d) self.assertEqual(x[0], i)
self.assertEqual(x[0].attr[1], x) self.assertEqual(x[0].attr, d)
self.assertEqual(x[0].attr[1][0], i) self.assertEqual(x[0].attr[1], x)
self.assertEqual(x[0].attr[1][0].attr, d) self.assertEqual(x[0].attr[1][0], i)
self.assertEqual(x[0].attr[1][0].attr, d)
def test_garyp(self): def test_garyp(self):
self.assertRaises(self.error, self.loads, 'garyp') self.assertRaises(self.error, self.loads, 'garyp')
@ -274,27 +425,30 @@ class AbstractPickleTests(unittest.TestCase):
def test_metaclass(self): def test_metaclass(self):
a = use_metaclass() a = use_metaclass()
s = self.dumps(a) for proto in protocols:
b = self.loads(s) s = self.dumps(a, proto)
self.assertEqual(a.__class__, b.__class__) b = self.loads(s)
self.assertEqual(a.__class__, b.__class__)
def test_structseq(self): def test_structseq(self):
import time import time
t = time.localtime()
s = self.dumps(t)
u = self.loads(s)
self.assertEqual(t, u)
import os import os
if hasattr(os, "stat"):
t = os.stat(os.curdir) t = time.localtime()
s = self.dumps(t) for proto in protocols:
u = self.loads(s) s = self.dumps(t, proto)
self.assertEqual(t, u)
if hasattr(os, "statvfs"):
t = os.statvfs(os.curdir)
s = self.dumps(t)
u = self.loads(s) u = self.loads(s)
self.assertEqual(t, u) self.assertEqual(t, u)
if hasattr(os, "stat"):
t = os.stat(os.curdir)
s = self.dumps(t, proto)
u = self.loads(s)
self.assertEqual(t, u)
if hasattr(os, "statvfs"):
t = os.statvfs(os.curdir)
s = self.dumps(t, proto)
u = self.loads(s)
self.assertEqual(t, u)
# Tests for protocol 2 # Tests for protocol 2
@ -356,9 +510,6 @@ class AbstractPickleTests(unittest.TestCase):
y = self.loads(s) y = self.loads(s)
self.assertEqual(tuple(x), tuple(y)) self.assertEqual(tuple(x), tuple(y))
self.assertEqual(x.__dict__, y.__dict__) self.assertEqual(x.__dict__, y.__dict__)
## import pickletools
## print
## pickletools.dis(s)
def test_newobj_list(self): def test_newobj_list(self):
x = MyList([1, 2, 3]) x = MyList([1, 2, 3])
@ -368,9 +519,6 @@ class AbstractPickleTests(unittest.TestCase):
y = self.loads(s) y = self.loads(s)
self.assertEqual(list(x), list(y)) self.assertEqual(list(x), list(y))
self.assertEqual(x.__dict__, y.__dict__) self.assertEqual(x.__dict__, y.__dict__)
## import pickletools
## print
## pickletools.dis(s)
def test_newobj_generic(self): def test_newobj_generic(self):
for proto in [0, 1, 2]: for proto in [0, 1, 2]:
@ -379,9 +527,6 @@ class AbstractPickleTests(unittest.TestCase):
x = C(C.sample) x = C(C.sample)
x.foo = 42 x.foo = 42
s = self.dumps(x, proto) s = self.dumps(x, proto)
## import pickletools
## print
## pickletools.dis(s)
y = self.loads(s) y = self.loads(s)
detail = (proto, C, B, x, y, type(y)) detail = (proto, C, B, x, y, type(y))
self.assertEqual(B(x), B(y), detail) self.assertEqual(B(x), B(y), detail)

View File

@ -719,6 +719,11 @@ put2(Picklerobject *self, PyObject *ob)
goto finally; goto finally;
/* Make sure memo keys are positive! */ /* Make sure memo keys are positive! */
/* XXX Why?
* XXX And does "positive" really mean non-negative?
* XXX pickle.py starts with PUT index 0, not 1. This makes for
* XXX gratuitous differences between the pickling modules.
*/
p++; p++;
if (!( py_ob_id = PyLong_FromVoidPtr(ob))) if (!( py_ob_id = PyLong_FromVoidPtr(ob)))