~ubuntu-branches/ubuntu/maverick/python3.1/maverick

« back to all changes in this revision

Viewing changes to Lib/copy.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-03-23 00:01:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090323000127-5fstfxju4ufrhthq
Tags: upstream-3.1~a1+20090322
ImportĀ upstreamĀ versionĀ 3.1~a1+20090322

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 copyreg 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, float, bool, str, tuple,
 
103
          frozenset, type, range,
 
104
          types.BuiltinFunctionType, type(Ellipsis),
 
105
          types.FunctionType):
 
106
    d[t] = _copy_immutable
 
107
t = getattr(types, "CodeType", None)
 
108
if t is not None:
 
109
    d[t] = _copy_immutable
 
110
for name in ("complex", "unicode"):
 
111
    t = globals()['__builtins__'].get(name)
 
112
    if t is not None:
 
113
        d[t] = _copy_immutable
 
114
 
 
115
def _copy_with_constructor(x):
 
116
    return type(x)(x)
 
117
for t in (list, dict, set):
 
118
    d[t] = _copy_with_constructor
 
119
 
 
120
def _copy_with_copy_method(x):
 
121
    return x.copy()
 
122
if PyStringMap is not None:
 
123
    d[PyStringMap] = _copy_with_copy_method
 
124
 
 
125
del d
 
126
 
 
127
def deepcopy(x, memo=None, _nil=[]):
 
128
    """Deep copy operation on arbitrary Python objects.
 
129
 
 
130
    See the module's __doc__ string for more info.
 
131
    """
 
132
 
 
133
    if memo is None:
 
134
        memo = {}
 
135
 
 
136
    d = id(x)
 
137
    y = memo.get(d, _nil)
 
138
    if y is not _nil:
 
139
        return y
 
140
 
 
141
    cls = type(x)
 
142
 
 
143
    copier = _deepcopy_dispatch.get(cls)
 
144
    if copier:
 
145
        y = copier(x, memo)
 
146
    else:
 
147
        try:
 
148
            issc = issubclass(cls, type)
 
149
        except TypeError: # cls is not a class (old Boost; see SF #502085)
 
150
            issc = 0
 
151
        if issc:
 
152
            y = _deepcopy_atomic(x, memo)
 
153
        else:
 
154
            copier = getattr(x, "__deepcopy__", None)
 
155
            if copier:
 
156
                y = copier(memo)
 
157
            else:
 
158
                reductor = dispatch_table.get(cls)
 
159
                if reductor:
 
160
                    rv = reductor(x)
 
161
                else:
 
162
                    reductor = getattr(x, "__reduce_ex__", None)
 
163
                    if reductor:
 
164
                        rv = reductor(2)
 
165
                    else:
 
166
                        reductor = getattr(x, "__reduce__", None)
 
167
                        if reductor:
 
168
                            rv = reductor()
 
169
                        else:
 
170
                            raise Error(
 
171
                                "un(deep)copyable object of type %s" % cls)
 
172
                y = _reconstruct(x, rv, 1, memo)
 
173
 
 
174
    memo[d] = y
 
175
    _keep_alive(x, memo) # Make sure x lives at least as long as d
 
176
    return y
 
177
 
 
178
_deepcopy_dispatch = d = {}
 
179
 
 
180
def _deepcopy_atomic(x, memo):
 
181
    return x
 
182
d[type(None)] = _deepcopy_atomic
 
183
d[type(Ellipsis)] = _deepcopy_atomic
 
184
d[int] = _deepcopy_atomic
 
185
d[float] = _deepcopy_atomic
 
186
d[bool] = _deepcopy_atomic
 
187
try:
 
188
    d[complex] = _deepcopy_atomic
 
189
except NameError:
 
190
    pass
 
191
d[bytes] = _deepcopy_atomic
 
192
d[str] = _deepcopy_atomic
 
193
try:
 
194
    d[types.CodeType] = _deepcopy_atomic
 
195
except AttributeError:
 
196
    pass
 
197
d[type] = _deepcopy_atomic
 
198
d[range] = _deepcopy_atomic
 
199
d[types.BuiltinFunctionType] = _deepcopy_atomic
 
200
d[types.FunctionType] = _deepcopy_atomic
 
201
 
 
202
def _deepcopy_list(x, memo):
 
203
    y = []
 
204
    memo[id(x)] = y
 
205
    for a in x:
 
206
        y.append(deepcopy(a, memo))
 
207
    return y
 
208
d[list] = _deepcopy_list
 
209
 
 
210
def _deepcopy_tuple(x, memo):
 
211
    y = []
 
212
    for a in x:
 
213
        y.append(deepcopy(a, memo))
 
214
    d = id(x)
 
215
    try:
 
216
        return memo[d]
 
217
    except KeyError:
 
218
        pass
 
219
    for i in range(len(x)):
 
220
        if x[i] is not y[i]:
 
221
            y = tuple(y)
 
222
            break
 
223
    else:
 
224
        y = x
 
225
    memo[d] = y
 
226
    return y
 
227
d[tuple] = _deepcopy_tuple
 
228
 
 
229
def _deepcopy_dict(x, memo):
 
230
    y = {}
 
231
    memo[id(x)] = y
 
232
    for key, value in x.items():
 
233
        y[deepcopy(key, memo)] = deepcopy(value, memo)
 
234
    return y
 
235
d[dict] = _deepcopy_dict
 
236
if PyStringMap is not None:
 
237
    d[PyStringMap] = _deepcopy_dict
 
238
 
 
239
def _keep_alive(x, memo):
 
240
    """Keeps a reference to the object x in the memo.
 
241
 
 
242
    Because we remember objects by their id, we have
 
243
    to assure that possibly temporary objects are kept
 
244
    alive by referencing them.
 
245
    We store a reference at the id of the memo, which should
 
246
    normally not be used unless someone tries to deepcopy
 
247
    the memo itself...
 
248
    """
 
249
    try:
 
250
        memo[id(memo)].append(x)
 
251
    except KeyError:
 
252
        # aha, this is the first one :-)
 
253
        memo[id(memo)]=[x]
 
254
 
 
255
def _reconstruct(x, info, deep, memo=None):
 
256
    if isinstance(info, str):
 
257
        return x
 
258
    assert isinstance(info, tuple)
 
259
    if memo is None:
 
260
        memo = {}
 
261
    n = len(info)
 
262
    assert n in (2, 3, 4, 5)
 
263
    callable, args = info[:2]
 
264
    if n > 2:
 
265
        state = info[2]
 
266
    else:
 
267
        state = {}
 
268
    if n > 3:
 
269
        listiter = info[3]
 
270
    else:
 
271
        listiter = None
 
272
    if n > 4:
 
273
        dictiter = info[4]
 
274
    else:
 
275
        dictiter = None
 
276
    if deep:
 
277
        args = deepcopy(args, memo)
 
278
    y = callable(*args)
 
279
    memo[id(x)] = y
 
280
    if listiter is not None:
 
281
        for item in listiter:
 
282
            if deep:
 
283
                item = deepcopy(item, memo)
 
284
            y.append(item)
 
285
    if dictiter is not None:
 
286
        for key, value in dictiter:
 
287
            if deep:
 
288
                key = deepcopy(key, memo)
 
289
                value = deepcopy(value, memo)
 
290
            y[key] = value
 
291
    if state:
 
292
        if deep:
 
293
            state = deepcopy(state, memo)
 
294
        if hasattr(y, '__setstate__'):
 
295
            y.__setstate__(state)
 
296
        else:
 
297
            if isinstance(state, tuple) and len(state) == 2:
 
298
                state, slotstate = state
 
299
            else:
 
300
                slotstate = None
 
301
            if state is not None:
 
302
                y.__dict__.update(state)
 
303
            if slotstate is not None:
 
304
                for key, value in slotstate.items():
 
305
                    setattr(y, key, value)
 
306
    return y
 
307
 
 
308
del d
 
309
 
 
310
del types
 
311
 
 
312
# Helper for instance creation without calling __init__
 
313
class _EmptyClass:
 
314
    pass
 
315
 
 
316
def _test():
 
317
    l = [None, 1, 2, 3.14, 'xyzzy', (1, 2), [3.14, 'abc'],
 
318
         {'abc': 'ABC'}, (), [], {}]
 
319
    l1 = copy(l)
 
320
    print(l1==l)
 
321
    l1 = map(copy, l)
 
322
    print(l1==l)
 
323
    l1 = deepcopy(l)
 
324
    print(l1==l)
 
325
    class C:
 
326
        def __init__(self, arg=None):
 
327
            self.a = 1
 
328
            self.arg = arg
 
329
            if __name__ == '__main__':
 
330
                import sys
 
331
                file = sys.argv[0]
 
332
            else:
 
333
                file = __file__
 
334
            self.fp = open(file)
 
335
            self.fp.close()
 
336
        def __getstate__(self):
 
337
            return {'a': self.a, 'arg': self.arg}
 
338
        def __setstate__(self, state):
 
339
            for key, value in state.items():
 
340
                setattr(self, key, value)
 
341
        def __deepcopy__(self, memo=None):
 
342
            new = self.__class__(deepcopy(self.arg, memo))
 
343
            new.a = self.a
 
344
            return new
 
345
    c = C('argument sketch')
 
346
    l.append(c)
 
347
    l2 = copy(l)
 
348
    print(l == l2)
 
349
    print(l)
 
350
    print(l2)
 
351
    l2 = deepcopy(l)
 
352
    print(l == l2)
 
353
    print(l)
 
354
    print(l2)
 
355
    l.append({l[1]: l, 'xyz': l[2]})
 
356
    l3 = copy(l)
 
357
    import reprlib
 
358
    print(map(reprlib.repr, l))
 
359
    print(map(reprlib.repr, l1))
 
360
    print(map(reprlib.repr, l2))
 
361
    print(map(reprlib.repr, l3))
 
362
    l3 = deepcopy(l)
 
363
    print(map(reprlib.repr, l))
 
364
    print(map(reprlib.repr, l1))
 
365
    print(map(reprlib.repr, l2))
 
366
    print(map(reprlib.repr, l3))
 
367
 
 
368
if __name__ == '__main__':
 
369
    _test()