Skip to content

Commit 8a23399

Browse files
Only share tested classes. Full support of 3.5+.
1 parent 7101adb commit 8a23399

File tree

5 files changed

+391
-390
lines changed

5 files changed

+391
-390
lines changed

Lib/test/picklecommon.py

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

0 commit comments

Comments
 (0)