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