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 any
43 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 import weakref
53 from copyreg import dispatch_table
54
55 class ESC[4;38;5;81mError(ESC[4;38;5;149mException):
56 pass
57 error = Error # backward compatibility
58
59 try:
60 from org.python.core import PyStringMap
61 except ImportError:
62 PyStringMap = None
63
64 __all__ = ["Error", "copy", "deepcopy"]
65
66 def copy(x):
67 """Shallow copy operation on arbitrary Python objects.
68
69 See the module's __doc__ string for more info.
70 """
71
72 cls = type(x)
73
74 copier = _copy_dispatch.get(cls)
75 if copier:
76 return copier(x)
77
78 if issubclass(cls, type):
79 # treat it as a regular class:
80 return _copy_immutable(x)
81
82 copier = getattr(cls, "__copy__", None)
83 if copier is not None:
84 return copier(x)
85
86 reductor = dispatch_table.get(cls)
87 if reductor is not None:
88 rv = reductor(x)
89 else:
90 reductor = getattr(x, "__reduce_ex__", None)
91 if reductor is not None:
92 rv = reductor(4)
93 else:
94 reductor = getattr(x, "__reduce__", None)
95 if reductor:
96 rv = reductor()
97 else:
98 raise Error("un(shallow)copyable object of type %s" % cls)
99
100 if isinstance(rv, str):
101 return x
102 return _reconstruct(x, None, *rv)
103
104
105 _copy_dispatch = d = {}
106
107 def _copy_immutable(x):
108 return x
109 for t in (type(None), int, float, bool, complex, str, tuple,
110 bytes, frozenset, type, range, slice, property,
111 types.BuiltinFunctionType, type(Ellipsis), type(NotImplemented),
112 types.FunctionType, weakref.ref):
113 d[t] = _copy_immutable
114 t = getattr(types, "CodeType", None)
115 if t is not None:
116 d[t] = _copy_immutable
117
118 d[list] = list.copy
119 d[dict] = dict.copy
120 d[set] = set.copy
121 d[bytearray] = bytearray.copy
122
123 if PyStringMap is not None:
124 d[PyStringMap] = PyStringMap.copy
125
126 del d, t
127
128 def deepcopy(x, memo=None, _nil=[]):
129 """Deep copy operation on arbitrary Python objects.
130
131 See the module's __doc__ string for more info.
132 """
133
134 if memo is None:
135 memo = {}
136
137 d = id(x)
138 y = memo.get(d, _nil)
139 if y is not _nil:
140 return y
141
142 cls = type(x)
143
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
149 y = _deepcopy_atomic(x, memo)
150 else:
151 copier = getattr(x, "__deepcopy__", None)
152 if copier is not None:
153 y = copier(memo)
154 else:
155 reductor = dispatch_table.get(cls)
156 if reductor:
157 rv = reductor(x)
158 else:
159 reductor = getattr(x, "__reduce_ex__", None)
160 if reductor is not None:
161 rv = reductor(4)
162 else:
163 reductor = getattr(x, "__reduce__", None)
164 if reductor:
165 rv = reductor()
166 else:
167 raise Error(
168 "un(deep)copyable object of type %s" % cls)
169 if isinstance(rv, str):
170 y = x
171 else:
172 y = _reconstruct(x, memo, *rv)
173
174 # If is its own copy, don't memoize.
175 if y is not x:
176 memo[d] = y
177 _keep_alive(x, memo) # Make sure x lives at least as long as d
178 return y
179
180 _deepcopy_dispatch = d = {}
181
182 def _deepcopy_atomic(x, memo):
183 return x
184 d[type(None)] = _deepcopy_atomic
185 d[type(Ellipsis)] = _deepcopy_atomic
186 d[type(NotImplemented)] = _deepcopy_atomic
187 d[int] = _deepcopy_atomic
188 d[float] = _deepcopy_atomic
189 d[bool] = _deepcopy_atomic
190 d[complex] = _deepcopy_atomic
191 d[bytes] = _deepcopy_atomic
192 d[str] = _deepcopy_atomic
193 d[types.CodeType] = _deepcopy_atomic
194 d[type] = _deepcopy_atomic
195 d[range] = _deepcopy_atomic
196 d[types.BuiltinFunctionType] = _deepcopy_atomic
197 d[types.FunctionType] = _deepcopy_atomic
198 d[weakref.ref] = _deepcopy_atomic
199 d[property] = _deepcopy_atomic
200
201 def _deepcopy_list(x, memo, deepcopy=deepcopy):
202 y = []
203 memo[id(x)] = y
204 append = y.append
205 for a in x:
206 append(deepcopy(a, memo))
207 return y
208 d[list] = _deepcopy_list
209
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
215 return memo[id(x)]
216 except KeyError:
217 pass
218 for k, j in zip(x, y):
219 if k is not j:
220 y = tuple(y)
221 break
222 else:
223 y = x
224 return y
225 d[tuple] = _deepcopy_tuple
226
227 def _deepcopy_dict(x, memo, deepcopy=deepcopy):
228 y = {}
229 memo[id(x)] = y
230 for key, value in x.items():
231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
233 d[dict] = _deepcopy_dict
234 if PyStringMap is not None:
235 d[PyStringMap] = _deepcopy_dict
236
237 def _deepcopy_method(x, memo): # Copy instance methods
238 return type(x)(x.__func__, deepcopy(x.__self__, memo))
239 d[types.MethodType] = _deepcopy_method
240
241 del d
242
243 def _keep_alive(x, memo):
244 """Keeps a reference to the object x in the memo.
245
246 Because we remember objects by their id, we have
247 to assure that possibly temporary objects are kept
248 alive by referencing them.
249 We store a reference at the id of the memo, which should
250 normally not be used unless someone tries to deepcopy
251 the memo itself...
252 """
253 try:
254 memo[id(memo)].append(x)
255 except KeyError:
256 # aha, this is the first one :-)
257 memo[id(memo)]=[x]
258
259 def _reconstruct(x, memo, func, args,
260 state=None, listiter=None, dictiter=None,
261 *, deepcopy=deepcopy):
262 deep = memo is not None
263 if deep and args:
264 args = (deepcopy(arg, memo) for arg in args)
265 y = func(*args)
266 if deep:
267 memo[id(x)] = y
268
269 if state is not None:
270 if deep:
271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)
274 else:
275 if isinstance(state, tuple) and len(state) == 2:
276 state, slotstate = state
277 else:
278 slotstate = None
279 if state is not None:
280 y.__dict__.update(state)
281 if slotstate is not None:
282 for key, value in slotstate.items():
283 setattr(y, key, value)
284
285 if listiter is not None:
286 if deep:
287 for item in listiter:
288 item = deepcopy(item, memo)
289 y.append(item)
290 else:
291 for item in listiter:
292 y.append(item)
293 if dictiter is not None:
294 if deep:
295 for key, value in dictiter:
296 key = deepcopy(key, memo)
297 value = deepcopy(value, memo)
298 y[key] = value
299 else:
300 for key, value in dictiter:
301 y[key] = value
302 return y
303
304 del types, weakref, PyStringMap