[IMP] type tests simplifications in ir_values.set
[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=[]):
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
176     d = id(x)
177     y = memo.get(d, _nil)
178     if y is not _nil:
179         return y
180
181     cls = type(x)
182
183     copier = _deepcopy_dispatch.get(cls)
184     if copier:
185         y = copier(x, memo)
186     else:
187         try:
188             issc = issubclass(cls, type)
189         except TypeError: # cls is not a class (old Boost; see SF #502085)
190             issc = 0
191         if issc:
192             y = _deepcopy_atomic(x, memo)
193         else:
194             copier = getattr(x, "__deepcopy__", None)
195             if copier:
196                 y = copier(memo)
197             else:
198                 reductor = dispatch_table.get(cls)
199                 if reductor:
200                     rv = reductor(x)
201                 else:
202                     reductor = getattr(x, "__reduce_ex__", None)
203                     if reductor:
204                         rv = reductor(2)
205                     else:
206                         reductor = getattr(x, "__reduce__", None)
207                         if reductor:
208                             rv = reductor()
209                         else:
210                             raise Error(
211                                 "un(deep)copyable object of type %s" % cls)
212                 y = _reconstruct(x, rv, 1, memo)
213
214     memo[d] = y
215     _keep_alive(x, memo) # Make sure x lives at least as long as d
216     return y
217
218 _deepcopy_dispatch = d = {}
219
220 def _deepcopy_atomic(x, memo):
221     return x
222 d[type(None)] = _deepcopy_atomic
223 d[int] = _deepcopy_atomic
224 d[long] = _deepcopy_atomic
225 d[float] = _deepcopy_atomic
226 d[bool] = _deepcopy_atomic
227 try:
228     d[complex] = _deepcopy_atomic
229 except NameError:
230     pass
231 d[str] = _deepcopy_atomic
232 try:
233     d[unicode] = _deepcopy_atomic
234 except NameError:
235     pass
236 try:
237     d[types.CodeType] = _deepcopy_atomic
238 except AttributeError:
239     pass
240 d[type] = _deepcopy_atomic
241 d[xrange] = _deepcopy_atomic
242 d[types.ClassType] = _deepcopy_atomic
243 d[types.BuiltinFunctionType] = _deepcopy_atomic
244 d[types.FunctionType] = _deepcopy_atomic
245
246 def _deepcopy_list(x, memo):
247     y = []
248     memo[id(x)] = y
249     for a in x:
250         y.append(deepcopy(a, memo))
251     return y
252 d[list] = _deepcopy_list
253
254 def _deepcopy_tuple(x, memo):
255     y = []
256     for a in x:
257         y.append(deepcopy(a, memo))
258     d = id(x)
259     try:
260         return memo[d]
261     except KeyError:
262         pass
263     for i in range(len(x)):
264         if x[i] is not y[i]:
265             y = tuple(y)
266             break
267     else:
268         y = x
269     memo[d] = y
270     return y
271 d[tuple] = _deepcopy_tuple
272
273 def _deepcopy_dict(x, memo):
274     y = {}
275     memo[id(x)] = y
276     for key, value in x.iteritems():
277         y[deepcopy(key, memo)] = deepcopy(value, memo)
278     return y
279 d[dict] = _deepcopy_dict
280 if PyStringMap is not None:
281     d[PyStringMap] = _deepcopy_dict
282
283 def _keep_alive(x, memo):
284     """Keeps a reference to the object x in the memo.
285
286     Because we remember objects by their id, we have
287     to assure that possibly temporary objects are kept
288     alive by referencing them.
289     We store a reference at the id of the memo, which should
290     normally not be used unless someone tries to deepcopy
291     the memo itself...
292     """
293     try:
294         memo[id(memo)].append(x)
295     except KeyError:
296         # aha, this is the first one :-)
297         memo[id(memo)]=[x]
298
299 def _deepcopy_inst(x, memo):
300     if hasattr(x, '__deepcopy__'):
301         return x.__deepcopy__(memo)
302     if hasattr(x, '__getinitargs__'):
303         args = x.__getinitargs__()
304         args = deepcopy(args, memo)
305         y = x.__class__(*args)
306     else:
307         y = _EmptyClass()
308         y.__class__ = x.__class__
309     memo[id(x)] = y
310     if hasattr(x, '__getstate__'):
311         state = x.__getstate__()
312     else:
313         state = x.__dict__
314     state = deepcopy(state, memo)
315     if hasattr(y, '__setstate__'):
316         y.__setstate__(state)
317     else:
318         y.__dict__.update(state)
319     return y
320 d[types.InstanceType] = _deepcopy_inst
321
322 def _reconstruct(x, info, deep, memo=None):
323     if isinstance(info, str):
324         return x
325     assert isinstance(info, tuple)
326     if memo is None:
327         memo = {}
328     n = len(info)
329     assert n in (2, 3, 4, 5)
330     callable, args = info[:2]
331     if n > 2:
332         state = info[2]
333     else:
334         state = {}
335     if n > 3:
336         listiter = info[3]
337     else:
338         listiter = None
339     if n > 4:
340         dictiter = info[4]
341     else:
342         dictiter = None
343     if deep:
344         args = deepcopy(args, memo)
345     y = callable(*args)
346     memo[id(x)] = y
347     if listiter is not None:
348         for item in listiter:
349             if deep:
350                 item = deepcopy(item, memo)
351             y.append(item)
352     if dictiter is not None:
353         for key, value in dictiter:
354             if deep:
355                 key = deepcopy(key, memo)
356                 value = deepcopy(value, memo)
357             y[key] = value
358     if state:
359         if deep:
360             state = deepcopy(state, memo)
361         if hasattr(y, '__setstate__'):
362             y.__setstate__(state)
363         else:
364             if isinstance(state, tuple) and len(state) == 2:
365                 state, slotstate = state
366             else:
367                 slotstate = None
368             if state is not None:
369                 y.__dict__.update(state)
370             if slotstate is not None:
371                 for key, value in slotstate.iteritems():
372                     setattr(y, key, value)
373     return y
374
375 del d
376
377 del types
378
379 # Helper for instance creation without calling __init__
380 class _EmptyClass:
381     pass
382
383 def _test():
384     l = [None, 1, 2L, 3.14, 'xyzzy', (1, 2L), [3.14, 'abc'],
385          {'abc': 'ABC'}, (), [], {}]
386     l1 = copy(l)
387     print l1==l
388     l1 = map(copy, l)
389     print l1==l
390     l1 = deepcopy(l)
391     print l1==l
392     class C:
393         def __init__(self, arg=None):
394             self.a = 1
395             self.arg = arg
396             if __name__ == '__main__':
397                 import sys
398                 file = sys.argv[0]
399             else:
400                 file = __file__
401             self.fp = open(file)
402             self.fp.close()
403         def __getstate__(self):
404             return {'a': self.a, 'arg': self.arg}
405         def __setstate__(self, state):
406             for key, value in state.iteritems():
407                 setattr(self, key, value)
408         def __deepcopy__(self, memo=None):
409             new = self.__class__(deepcopy(self.arg, memo))
410             new.a = self.a
411             return new
412     c = C('argument sketch')
413     l.append(c)
414     l2 = copy(l)
415     print l == l2
416     print l
417     print l2
418     l2 = deepcopy(l)
419     print l == l2
420     print l
421     print l2
422     l.append({l[1]: l, 'xyz': l[2]})
423     l3 = copy(l)
424     import repr
425     print map(repr.repr, l)
426     print map(repr.repr, l1)
427     print map(repr.repr, l2)
428     print map(repr.repr, l3)
429     l3 = deepcopy(l)
430     import repr
431     print map(repr.repr, l)
432     print map(repr.repr, l1)
433     print map(repr.repr, l2)
434     print map(repr.repr, l3)
435
436 if __name__ == '__main__':
437     _test()