Skip to content

Commit 9812845

Browse files
miss-islingtonFidget-Spinnerserhiy-storchaka
authored
[3.14] gh-75572: Forward-port test_xpickle from Python 2 to 3 (GH-22452) (GH-143485)
Move data classes used in tests to separate file test_picklecommon.py, so it can be imported in old Python versions. (cherry picked from commit 8735daf) Co-authored-by: Ken Jin <kenjin@python.org> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 22a99ca commit 9812845

File tree

7 files changed

+1023
-506
lines changed

7 files changed

+1023
-506
lines changed

Lib/test/libregrtest/cmdline.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@
130130
131131
tzdata - Run tests that require timezone data.
132132
133+
xpickle - Test pickle and _pickle against Python 3.6, 3.7, 3.8
134+
and 3.9 to test backwards compatibility. These tests
135+
may take very long to complete.
136+
133137
To enable all resources except one, use '-uall,-<resource>'. For
134138
example, to run all the tests except for the gui tests, give the
135139
option '-uall,-gui'.

Lib/test/libregrtest/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
# - tzdata: while needed to validate fully test_datetime, it makes
4242
# test_datetime too slow (15-20 min on some buildbots) and so is disabled by
4343
# default (see bpo-30822).
44-
RESOURCE_NAMES = ALL_RESOURCES + ('extralargefile', 'tzdata')
44+
RESOURCE_NAMES = ALL_RESOURCES + ('extralargefile', 'tzdata', 'xpickle')
4545

4646

4747
# Types for types hints

Lib/test/picklecommon.py

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
# Classes used for pickle testing.
2+
# They are moved to separate file, so they can be loaded
3+
# in other Python version for test_xpickle.
4+
5+
import sys
6+
7+
class C:
8+
def __eq__(self, other):
9+
return self.__dict__ == other.__dict__
10+
11+
# For test_load_classic_instance
12+
class D(C):
13+
def __init__(self, arg):
14+
pass
15+
16+
class E(C):
17+
def __getinitargs__(self):
18+
return ()
19+
20+
import __main__
21+
__main__.C = C
22+
C.__module__ = "__main__"
23+
__main__.D = D
24+
D.__module__ = "__main__"
25+
__main__.E = E
26+
E.__module__ = "__main__"
27+
28+
# Simple mutable object.
29+
class Object:
30+
pass
31+
32+
# Hashable immutable key object containing unheshable mutable data.
33+
class K:
34+
def __init__(self, value):
35+
self.value = value
36+
37+
def __reduce__(self):
38+
# Shouldn't support the recursion itself
39+
return K, (self.value,)
40+
41+
# For test_misc
42+
class myint(int):
43+
def __init__(self, x):
44+
self.str = str(x)
45+
46+
# For test_misc and test_getinitargs
47+
class initarg(C):
48+
49+
def __init__(self, a, b):
50+
self.a = a
51+
self.b = b
52+
53+
def __getinitargs__(self):
54+
return self.a, self.b
55+
56+
# For test_metaclass
57+
class metaclass(type):
58+
pass
59+
60+
if sys.version_info >= (3,):
61+
# Syntax not compatible with Python 2
62+
exec('''
63+
class use_metaclass(object, metaclass=metaclass):
64+
pass
65+
''')
66+
else:
67+
class use_metaclass(object):
68+
__metaclass__ = metaclass
69+
70+
71+
# Test classes for reduce_ex
72+
73+
class R:
74+
def __init__(self, reduce=None):
75+
self.reduce = reduce
76+
def __reduce__(self, proto):
77+
return self.reduce
78+
79+
class REX:
80+
def __init__(self, reduce_ex=None):
81+
self.reduce_ex = reduce_ex
82+
def __reduce_ex__(self, proto):
83+
return self.reduce_ex
84+
85+
class REX_one(object):
86+
"""No __reduce_ex__ here, but inheriting it from object"""
87+
_reduce_called = 0
88+
def __reduce__(self):
89+
self._reduce_called = 1
90+
return REX_one, ()
91+
92+
class REX_two(object):
93+
"""No __reduce__ here, but inheriting it from object"""
94+
_proto = None
95+
def __reduce_ex__(self, proto):
96+
self._proto = proto
97+
return REX_two, ()
98+
99+
class REX_three(object):
100+
_proto = None
101+
def __reduce_ex__(self, proto):
102+
self._proto = proto
103+
return REX_two, ()
104+
def __reduce__(self):
105+
raise AssertionError("This __reduce__ shouldn't be called")
106+
107+
class REX_four(object):
108+
"""Calling base class method should succeed"""
109+
_proto = None
110+
def __reduce_ex__(self, proto):
111+
self._proto = proto
112+
return object.__reduce_ex__(self, proto)
113+
114+
class REX_five(object):
115+
"""This one used to fail with infinite recursion"""
116+
_reduce_called = 0
117+
def __reduce__(self):
118+
self._reduce_called = 1
119+
return object.__reduce__(self)
120+
121+
class REX_six(object):
122+
"""This class is used to check the 4th argument (list iterator) of
123+
the reduce protocol.
124+
"""
125+
def __init__(self, items=None):
126+
self.items = items if items is not None else []
127+
def __eq__(self, other):
128+
return type(self) is type(other) and self.items == other.items
129+
def append(self, item):
130+
self.items.append(item)
131+
def __reduce__(self):
132+
return type(self), (), None, iter(self.items), None
133+
134+
class REX_seven(object):
135+
"""This class is used to check the 5th argument (dict iterator) of
136+
the reduce protocol.
137+
"""
138+
def __init__(self, table=None):
139+
self.table = table if table is not None else {}
140+
def __eq__(self, other):
141+
return type(self) is type(other) and self.table == other.table
142+
def __setitem__(self, key, value):
143+
self.table[key] = value
144+
def __reduce__(self):
145+
return type(self), (), None, None, iter(self.table.items())
146+
147+
class REX_state(object):
148+
"""This class is used to check the 3th argument (state) of
149+
the reduce protocol.
150+
"""
151+
def __init__(self, state=None):
152+
self.state = state
153+
def __eq__(self, other):
154+
return type(self) is type(other) and self.state == other.state
155+
def __setstate__(self, state):
156+
self.state = state
157+
def __reduce__(self):
158+
return type(self), (), self.state
159+
160+
# For test_reduce_ex_None
161+
class REX_None:
162+
""" Setting __reduce_ex__ to None should fail """
163+
__reduce_ex__ = None
164+
165+
# For test_reduce_None
166+
class R_None:
167+
""" Setting __reduce__ to None should fail """
168+
__reduce__ = None
169+
170+
# For test_pickle_setstate_None
171+
class C_None_setstate:
172+
""" Setting __setstate__ to None should fail """
173+
def __getstate__(self):
174+
return 1
175+
176+
__setstate__ = None
177+
178+
179+
# Test classes for newobj
180+
181+
# For test_newobj_generic and test_newobj_proxies
182+
183+
class MyInt(int):
184+
sample = 1
185+
186+
if sys.version_info >= (3,):
187+
class MyLong(int):
188+
sample = 1
189+
else:
190+
class MyLong(long):
191+
sample = long(1)
192+
193+
class MyFloat(float):
194+
sample = 1.0
195+
196+
class MyComplex(complex):
197+
sample = 1.0 + 0.0j
198+
199+
class MyStr(str):
200+
sample = "hello"
201+
202+
if sys.version_info >= (3,):
203+
class MyUnicode(str):
204+
sample = "hello \u1234"
205+
else:
206+
class MyUnicode(unicode):
207+
sample = unicode(r"hello \u1234", "raw-unicode-escape")
208+
209+
class MyTuple(tuple):
210+
sample = (1, 2, 3)
211+
212+
class MyList(list):
213+
sample = [1, 2, 3]
214+
215+
class MyDict(dict):
216+
sample = {"a": 1, "b": 2}
217+
218+
class MySet(set):
219+
sample = {"a", "b"}
220+
221+
class MyFrozenSet(frozenset):
222+
sample = frozenset({"a", "b"})
223+
224+
myclasses = [MyInt, MyLong, MyFloat,
225+
MyComplex,
226+
MyStr, MyUnicode,
227+
MyTuple, MyList, MyDict, MySet, MyFrozenSet]
228+
229+
# For test_newobj_overridden_new
230+
class MyIntWithNew(int):
231+
def __new__(cls, value):
232+
raise AssertionError
233+
234+
class MyIntWithNew2(MyIntWithNew):
235+
__new__ = int.__new__
236+
237+
238+
# For test_newobj_list_slots
239+
class SlotList(MyList):
240+
__slots__ = ["foo"]
241+
242+
# Ruff "redefined while unused" false positive here due to `global` variables
243+
# being assigned (and then restored) from within test methods earlier in the file
244+
class SimpleNewObj(int): # noqa: F811
245+
def __init__(self, *args, **kwargs):
246+
# raise an error, to make sure this isn't called
247+
raise TypeError("SimpleNewObj.__init__() didn't expect to get called")
248+
def __eq__(self, other):
249+
return int(self) == int(other) and self.__dict__ == other.__dict__
250+
251+
class ComplexNewObj(SimpleNewObj):
252+
def __getnewargs__(self):
253+
return ('%X' % self, 16)
254+
255+
class ComplexNewObjEx(SimpleNewObj):
256+
def __getnewargs_ex__(self):
257+
return ('%X' % self,), {'base': 16}
258+
259+
260+
class ZeroCopyBytes(bytes):
261+
readonly = True
262+
c_contiguous = True
263+
f_contiguous = True
264+
zero_copy_reconstruct = True
265+
266+
def __reduce_ex__(self, protocol):
267+
if protocol >= 5:
268+
import pickle
269+
return type(self)._reconstruct, (pickle.PickleBuffer(self),), None
270+
else:
271+
return type(self)._reconstruct, (bytes(self),)
272+
273+
def __repr__(self):
274+
return "{}({!r})".format(self.__class__.__name__, bytes(self))
275+
276+
__str__ = __repr__
277+
278+
@classmethod
279+
def _reconstruct(cls, obj):
280+
with memoryview(obj) as m:
281+
obj = m.obj
282+
if type(obj) is cls:
283+
# Zero-copy
284+
return obj
285+
else:
286+
return cls(obj)
287+
288+
289+
class ZeroCopyBytearray(bytearray):
290+
readonly = False
291+
c_contiguous = True
292+
f_contiguous = True
293+
zero_copy_reconstruct = True
294+
295+
def __reduce_ex__(self, protocol):
296+
if protocol >= 5:
297+
import pickle
298+
return type(self)._reconstruct, (pickle.PickleBuffer(self),), None
299+
else:
300+
return type(self)._reconstruct, (bytes(self),)
301+
302+
def __repr__(self):
303+
return "{}({!r})".format(self.__class__.__name__, bytes(self))
304+
305+
__str__ = __repr__
306+
307+
@classmethod
308+
def _reconstruct(cls, obj):
309+
with memoryview(obj) as m:
310+
obj = m.obj
311+
if type(obj) is cls:
312+
# Zero-copy
313+
return obj
314+
else:
315+
return cls(obj)
316+
317+
318+
# For test_nested_names
319+
class Nested:
320+
class A:
321+
class B:
322+
class C:
323+
pass
324+
325+
# For test_py_methods
326+
class PyMethodsTest:
327+
@staticmethod
328+
def cheese():
329+
return "cheese"
330+
@classmethod
331+
def wine(cls):
332+
assert cls is PyMethodsTest
333+
return "wine"
334+
def biscuits(self):
335+
assert isinstance(self, PyMethodsTest)
336+
return "biscuits"
337+
class Nested:
338+
"Nested class"
339+
@staticmethod
340+
def ketchup():
341+
return "ketchup"
342+
@classmethod
343+
def maple(cls):
344+
assert cls is PyMethodsTest.Nested
345+
return "maple"
346+
def pie(self):
347+
assert isinstance(self, PyMethodsTest.Nested)
348+
return "pie"
349+
350+
# For test_c_methods
351+
class Subclass(tuple):
352+
class Nested(str):
353+
pass

0 commit comments

Comments
 (0)