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