OILS / opy / _regtest / src / copy.py View on Github | oilshell.org

434 lines, 327 significant
1from __future__ import print_function # for OPy compiler
2"""Generic (shallow and deep) copying operations.
3
4Interface summary:
5
6 import copy
7
8 x = copy.copy(y) # make a shallow copy of y
9 x = copy.deepcopy(y) # make a deep copy of y
10
11For module specific errors, copy.Error is raised.
12
13The difference between shallow and deep copying is only relevant for
14compound objects (objects that contain other objects, like lists or
15class instances).
16
17- A shallow copy constructs a new compound object and then (to the
18 extent possible) inserts *the same objects* into it that the
19 original contains.
20
21- A deep copy constructs a new compound object and then, recursively,
22 inserts *copies* into it of the objects found in the original.
23
24Two problems often exist with deep copy operations that don't exist
25with shallow copy operations:
26
27 a) recursive objects (compound objects that, directly or indirectly,
28 contain a reference to themselves) may cause a recursive loop
29
30 b) because deep copy copies *everything* it may copy too much, e.g.
31 administrative data structures that should be shared even between
32 copies
33
34Python's deep copy operation avoids these problems by:
35
36 a) keeping a table of objects already copied during the current
37 copying pass
38
39 b) letting user-defined classes override the copying operation or the
40 set of components copied
41
42This version does not copy types like module, class, function, method,
43nor stack trace, stack frame, nor file, socket, window, nor array, nor
44any similar types.
45
46Classes can use the same interfaces to control copying that they use
47to control pickling: they can define methods called __getinitargs__(),
48__getstate__() and __setstate__(). See the documentation for module
49"pickle" for information on these methods.
50"""
51
52import types
53import weakref
54from copy_reg import dispatch_table
55
56class Error(Exception):
57 pass
58error = Error # backward compatibility
59
60try:
61 from org.python.core import PyStringMap
62except ImportError:
63 PyStringMap = None
64
65__all__ = ["Error", "copy", "deepcopy"]
66
67def copy(x):
68 """Shallow copy operation on arbitrary Python objects.
69
70 See the module's __doc__ string for more info.
71 """
72
73 cls = type(x)
74
75 copier = _copy_dispatch.get(cls)
76 if copier:
77 return copier(x)
78
79 copier = getattr(cls, "__copy__", None)
80 if copier:
81 return copier(x)
82
83 reductor = dispatch_table.get(cls)
84 if reductor:
85 rv = reductor(x)
86 else:
87 reductor = getattr(x, "__reduce_ex__", None)
88 if reductor:
89 rv = reductor(2)
90 else:
91 reductor = getattr(x, "__reduce__", None)
92 if reductor:
93 rv = reductor()
94 else:
95 raise Error("un(shallow)copyable object of type %s" % cls)
96
97 return _reconstruct(x, rv, 0)
98
99
100_copy_dispatch = d = {}
101
102def _copy_immutable(x):
103 return x
104for t in (type(None), int, long, float, bool, str, tuple,
105 frozenset, type, xrange, types.ClassType,
106 types.BuiltinFunctionType, type(Ellipsis),
107 types.FunctionType, weakref.ref):
108 d[t] = _copy_immutable
109for name in ("ComplexType", "UnicodeType", "CodeType"):
110 t = getattr(types, name, None)
111 if t is not None:
112 d[t] = _copy_immutable
113
114def _copy_with_constructor(x):
115 return type(x)(x)
116for t in (list, dict, set):
117 d[t] = _copy_with_constructor
118
119def _copy_with_copy_method(x):
120 return x.copy()
121if PyStringMap is not None:
122 d[PyStringMap] = _copy_with_copy_method
123
124def _copy_inst(x):
125 if hasattr(x, '__copy__'):
126 return x.__copy__()
127 if hasattr(x, '__getinitargs__'):
128 args = x.__getinitargs__()
129 y = x.__class__(*args)
130 else:
131 y = _EmptyClass()
132 y.__class__ = x.__class__
133 if hasattr(x, '__getstate__'):
134 state = x.__getstate__()
135 else:
136 state = x.__dict__
137 if hasattr(y, '__setstate__'):
138 y.__setstate__(state)
139 else:
140 y.__dict__.update(state)
141 return y
142d[types.InstanceType] = _copy_inst
143
144del d
145
146def deepcopy(x, memo=None, _nil=[]):
147 """Deep copy operation on arbitrary Python objects.
148
149 See the module's __doc__ string for more info.
150 """
151
152 if memo is None:
153 memo = {}
154
155 d = id(x)
156 y = memo.get(d, _nil)
157 if y is not _nil:
158 return y
159
160 cls = type(x)
161
162 copier = _deepcopy_dispatch.get(cls)
163 if copier:
164 y = copier(x, memo)
165 else:
166 try:
167 issc = issubclass(cls, type)
168 except TypeError: # cls is not a class (old Boost; see SF #502085)
169 issc = 0
170 if issc:
171 y = _deepcopy_atomic(x, memo)
172 else:
173 copier = getattr(x, "__deepcopy__", None)
174 if copier:
175 y = copier(memo)
176 else:
177 reductor = dispatch_table.get(cls)
178 if reductor:
179 rv = reductor(x)
180 else:
181 reductor = getattr(x, "__reduce_ex__", None)
182 if reductor:
183 rv = reductor(2)
184 else:
185 reductor = getattr(x, "__reduce__", None)
186 if reductor:
187 rv = reductor()
188 else:
189 raise Error(
190 "un(deep)copyable object of type %s" % cls)
191 y = _reconstruct(x, rv, 1, memo)
192
193 memo[d] = y
194 _keep_alive(x, memo) # Make sure x lives at least as long as d
195 return y
196
197_deepcopy_dispatch = d = {}
198
199def _deepcopy_atomic(x, memo):
200 return x
201d[type(None)] = _deepcopy_atomic
202d[type(Ellipsis)] = _deepcopy_atomic
203d[int] = _deepcopy_atomic
204d[long] = _deepcopy_atomic
205d[float] = _deepcopy_atomic
206d[bool] = _deepcopy_atomic
207try:
208 d[complex] = _deepcopy_atomic
209except NameError:
210 pass
211d[str] = _deepcopy_atomic
212try:
213 d[unicode] = _deepcopy_atomic
214except NameError:
215 pass
216try:
217 d[types.CodeType] = _deepcopy_atomic
218except AttributeError:
219 pass
220d[type] = _deepcopy_atomic
221d[xrange] = _deepcopy_atomic
222d[types.ClassType] = _deepcopy_atomic
223d[types.BuiltinFunctionType] = _deepcopy_atomic
224d[types.FunctionType] = _deepcopy_atomic
225d[weakref.ref] = _deepcopy_atomic
226
227def _deepcopy_list(x, memo):
228 y = []
229 memo[id(x)] = y
230 for a in x:
231 y.append(deepcopy(a, memo))
232 return y
233d[list] = _deepcopy_list
234
235def _deepcopy_tuple(x, memo):
236 y = []
237 for a in x:
238 y.append(deepcopy(a, memo))
239 d = id(x)
240 try:
241 return memo[d]
242 except KeyError:
243 pass
244 for i in range(len(x)):
245 if x[i] is not y[i]:
246 y = tuple(y)
247 break
248 else:
249 y = x
250 memo[d] = y
251 return y
252d[tuple] = _deepcopy_tuple
253
254def _deepcopy_dict(x, memo):
255 y = {}
256 memo[id(x)] = y
257 for key, value in x.iteritems():
258 y[deepcopy(key, memo)] = deepcopy(value, memo)
259 return y
260d[dict] = _deepcopy_dict
261if PyStringMap is not None:
262 d[PyStringMap] = _deepcopy_dict
263
264def _deepcopy_method(x, memo): # Copy instance methods
265 return type(x)(x.im_func, deepcopy(x.im_self, memo), x.im_class)
266_deepcopy_dispatch[types.MethodType] = _deepcopy_method
267
268def _keep_alive(x, memo):
269 """Keeps a reference to the object x in the memo.
270
271 Because we remember objects by their id, we have
272 to assure that possibly temporary objects are kept
273 alive by referencing them.
274 We store a reference at the id of the memo, which should
275 normally not be used unless someone tries to deepcopy
276 the memo itself...
277 """
278 try:
279 memo[id(memo)].append(x)
280 except KeyError:
281 # aha, this is the first one :-)
282 memo[id(memo)]=[x]
283
284def _deepcopy_inst(x, memo):
285 if hasattr(x, '__deepcopy__'):
286 return x.__deepcopy__(memo)
287 if hasattr(x, '__getinitargs__'):
288 args = x.__getinitargs__()
289 args = deepcopy(args, memo)
290 y = x.__class__(*args)
291 else:
292 y = _EmptyClass()
293 y.__class__ = x.__class__
294 memo[id(x)] = y
295 if hasattr(x, '__getstate__'):
296 state = x.__getstate__()
297 else:
298 state = x.__dict__
299 state = deepcopy(state, memo)
300 if hasattr(y, '__setstate__'):
301 y.__setstate__(state)
302 else:
303 y.__dict__.update(state)
304 return y
305d[types.InstanceType] = _deepcopy_inst
306
307def _reconstruct(x, info, deep, memo=None):
308 if isinstance(info, str):
309 return x
310 assert isinstance(info, tuple)
311 if memo is None:
312 memo = {}
313 n = len(info)
314 assert n in (2, 3, 4, 5)
315 callable, args = info[:2]
316 if n > 2:
317 state = info[2]
318 else:
319 state = None
320 if n > 3:
321 listiter = info[3]
322 else:
323 listiter = None
324 if n > 4:
325 dictiter = info[4]
326 else:
327 dictiter = None
328 if deep:
329 args = deepcopy(args, memo)
330 y = callable(*args)
331 memo[id(x)] = y
332
333 if state is not None:
334 if deep:
335 state = deepcopy(state, memo)
336 if hasattr(y, '__setstate__'):
337 y.__setstate__(state)
338 else:
339 if isinstance(state, tuple) and len(state) == 2:
340 state, slotstate = state
341 else:
342 slotstate = None
343 if state is not None:
344 y.__dict__.update(state)
345 if slotstate is not None:
346 for key, value in slotstate.iteritems():
347 setattr(y, key, value)
348
349 if listiter is not None:
350 for item in listiter:
351 if deep:
352 item = deepcopy(item, memo)
353 y.append(item)
354 if dictiter is not None:
355 for key, value in dictiter:
356 if deep:
357 key = deepcopy(key, memo)
358 value = deepcopy(value, memo)
359 y[key] = value
360 return y
361
362del d
363
364del types
365
366# Helper for instance creation without calling __init__
367class _EmptyClass:
368 pass
369
370def _test():
371 l = [None, 1, 2L, 3.14, 'xyzzy', (1, 2L), [3.14, 'abc'],
372 {'abc': 'ABC'}, (), [], {}]
373 l1 = copy(l)
374 print(l1==l)
375 l1 = map(copy, l)
376 print(l1==l)
377 l1 = deepcopy(l)
378 print(l1==l)
379 class C:
380 def __init__(self, arg=None):
381 self.a = 1
382 self.arg = arg
383 if __name__ == '__main__':
384 import sys
385 file = sys.argv[0]
386 else:
387 file = __file__
388 self.fp = open(file)
389 self.fp.close()
390 def __getstate__(self):
391 return {'a': self.a, 'arg': self.arg}
392 def __setstate__(self, state):
393 for key, value in state.iteritems():
394 setattr(self, key, value)
395 def __deepcopy__(self, memo=None):
396 new = self.__class__(deepcopy(self.arg, memo))
397 new.a = self.a
398 return new
399 c = C('argument sketch')
400 l.append(c)
401 l2 = copy(l)
402 print(l == l2)
403 print(l)
404 print(l2)
405 l2 = deepcopy(l)
406 print(l == l2)
407 print(l)
408 print(l2)
409 l.append({l[1]: l, 'xyz': l[2]})
410 l3 = copy(l)
411 import repr
412 print(map(repr.repr, l))
413 print(map(repr.repr, l1))
414 print(map(repr.repr, l2))
415 print(map(repr.repr, l3))
416 l3 = deepcopy(l)
417 import repr
418 print(map(repr.repr, l))
419 print(map(repr.repr, l1))
420 print(map(repr.repr, l2))
421 print(map(repr.repr, l3))
422 class odict(dict):
423 def __init__(self, d = {}):
424 self.a = 99
425 dict.__init__(self, d)
426 def __setitem__(self, k, i):
427 dict.__setitem__(self, k, i)
428 self.a
429 o = odict({"A" : "B"})
430 x = deepcopy(o)
431 print((o, x))
432
433if __name__ == '__main__':
434 _test()