• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Generic (shallow and deep) copying operations.
2
3Interface 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        x = copy.replace(y, a=1, b=2)   # new object with fields replaced, as defined by `__replace__`
10
11For module specific errors, copy.Error is raised.
12
13The difference between shallow and deep copying is only relevant for
14compound objects (objects that contain other objects, like lists or
15class instances).
16
17- A shallow copy constructs a new compound object and then (to the
18  extent possible) inserts *the same objects* into it that the
19  original contains.
20
21- A deep copy constructs a new compound object and then, recursively,
22  inserts *copies* into it of the objects found in the original.
23
24Two problems often exist with deep copy operations that don't exist
25with shallow copy operations:
26
27 a) recursive objects (compound objects that, directly or indirectly,
28    contain a reference to themselves) may cause a recursive loop
29
30 b) because deep copy copies *everything* it may copy too much, e.g.
31    administrative data structures that should be shared even between
32    copies
33
34Python's deep copy operation avoids these problems by:
35
36 a) keeping a table of objects already copied during the current
37    copying pass
38
39 b) letting user-defined classes override the copying operation or the
40    set of components copied
41
42This version does not copy types like module, class, function, method,
43nor stack trace, stack frame, nor file, socket, window, nor any
44similar types.
45
46Classes can use the same interfaces to control copying that they use
47to control pickling: they can define methods called __getinitargs__(),
48__getstate__() and __setstate__().  See the documentation for module
49"pickle" for information on these methods.
50"""
51
52import types
53import weakref
54from copyreg import dispatch_table
55
56class Error(Exception):
57    pass
58error = Error   # backward compatibility
59
60__all__ = ["Error", "copy", "deepcopy", "replace"]
61
62def copy(x):
63    """Shallow copy operation on arbitrary Python objects.
64
65    See the module's __doc__ string for more info.
66    """
67
68    cls = type(x)
69
70    copier = _copy_dispatch.get(cls)
71    if copier:
72        return copier(x)
73
74    if issubclass(cls, type):
75        # treat it as a regular class:
76        return _copy_immutable(x)
77
78    copier = getattr(cls, "__copy__", None)
79    if copier is not None:
80        return copier(x)
81
82    reductor = dispatch_table.get(cls)
83    if reductor is not None:
84        rv = reductor(x)
85    else:
86        reductor = getattr(x, "__reduce_ex__", None)
87        if reductor is not None:
88            rv = reductor(4)
89        else:
90            reductor = getattr(x, "__reduce__", None)
91            if reductor:
92                rv = reductor()
93            else:
94                raise Error("un(shallow)copyable object of type %s" % cls)
95
96    if isinstance(rv, str):
97        return x
98    return _reconstruct(x, None, *rv)
99
100
101_copy_dispatch = d = {}
102
103def _copy_immutable(x):
104    return x
105for t in (types.NoneType, int, float, bool, complex, str, tuple,
106          bytes, frozenset, type, range, slice, property,
107          types.BuiltinFunctionType, types.EllipsisType,
108          types.NotImplementedType, types.FunctionType, types.CodeType,
109          weakref.ref):
110    d[t] = _copy_immutable
111
112d[list] = list.copy
113d[dict] = dict.copy
114d[set] = set.copy
115d[bytearray] = bytearray.copy
116
117del d, t
118
119def deepcopy(x, memo=None, _nil=[]):
120    """Deep copy operation on arbitrary Python objects.
121
122    See the module's __doc__ string for more info.
123    """
124
125    d = id(x)
126    if memo is None:
127        memo = {}
128    else:
129        y = memo.get(d, _nil)
130        if y is not _nil:
131            return y
132
133    cls = type(x)
134
135    copier = _deepcopy_dispatch.get(cls)
136    if copier is not None:
137        y = copier(x, memo)
138    else:
139        if issubclass(cls, type):
140            y = _deepcopy_atomic(x, memo)
141        else:
142            copier = getattr(x, "__deepcopy__", None)
143            if copier is not None:
144                y = copier(memo)
145            else:
146                reductor = dispatch_table.get(cls)
147                if reductor:
148                    rv = reductor(x)
149                else:
150                    reductor = getattr(x, "__reduce_ex__", None)
151                    if reductor is not None:
152                        rv = reductor(4)
153                    else:
154                        reductor = getattr(x, "__reduce__", None)
155                        if reductor:
156                            rv = reductor()
157                        else:
158                            raise Error(
159                                "un(deep)copyable object of type %s" % cls)
160                if isinstance(rv, str):
161                    y = x
162                else:
163                    y = _reconstruct(x, memo, *rv)
164
165    # If is its own copy, don't memoize.
166    if y is not x:
167        memo[d] = y
168        _keep_alive(x, memo) # Make sure x lives at least as long as d
169    return y
170
171_deepcopy_dispatch = d = {}
172
173def _deepcopy_atomic(x, memo):
174    return x
175d[types.NoneType] = _deepcopy_atomic
176d[types.EllipsisType] = _deepcopy_atomic
177d[types.NotImplementedType] = _deepcopy_atomic
178d[int] = _deepcopy_atomic
179d[float] = _deepcopy_atomic
180d[bool] = _deepcopy_atomic
181d[complex] = _deepcopy_atomic
182d[bytes] = _deepcopy_atomic
183d[str] = _deepcopy_atomic
184d[types.CodeType] = _deepcopy_atomic
185d[type] = _deepcopy_atomic
186d[range] = _deepcopy_atomic
187d[types.BuiltinFunctionType] = _deepcopy_atomic
188d[types.FunctionType] = _deepcopy_atomic
189d[weakref.ref] = _deepcopy_atomic
190d[property] = _deepcopy_atomic
191
192def _deepcopy_list(x, memo, deepcopy=deepcopy):
193    y = []
194    memo[id(x)] = y
195    append = y.append
196    for a in x:
197        append(deepcopy(a, memo))
198    return y
199d[list] = _deepcopy_list
200
201def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
202    y = [deepcopy(a, memo) for a in x]
203    # We're not going to put the tuple in the memo, but it's still important we
204    # check for it, in case the tuple contains recursive mutable structures.
205    try:
206        return memo[id(x)]
207    except KeyError:
208        pass
209    for k, j in zip(x, y):
210        if k is not j:
211            y = tuple(y)
212            break
213    else:
214        y = x
215    return y
216d[tuple] = _deepcopy_tuple
217
218def _deepcopy_dict(x, memo, deepcopy=deepcopy):
219    y = {}
220    memo[id(x)] = y
221    for key, value in x.items():
222        y[deepcopy(key, memo)] = deepcopy(value, memo)
223    return y
224d[dict] = _deepcopy_dict
225
226def _deepcopy_method(x, memo): # Copy instance methods
227    return type(x)(x.__func__, deepcopy(x.__self__, memo))
228d[types.MethodType] = _deepcopy_method
229
230del d
231
232def _keep_alive(x, memo):
233    """Keeps a reference to the object x in the memo.
234
235    Because we remember objects by their id, we have
236    to assure that possibly temporary objects are kept
237    alive by referencing them.
238    We store a reference at the id of the memo, which should
239    normally not be used unless someone tries to deepcopy
240    the memo itself...
241    """
242    try:
243        memo[id(memo)].append(x)
244    except KeyError:
245        # aha, this is the first one :-)
246        memo[id(memo)]=[x]
247
248def _reconstruct(x, memo, func, args,
249                 state=None, listiter=None, dictiter=None,
250                 *, deepcopy=deepcopy):
251    deep = memo is not None
252    if deep and args:
253        args = (deepcopy(arg, memo) for arg in args)
254    y = func(*args)
255    if deep:
256        memo[id(x)] = y
257
258    if state is not None:
259        if deep:
260            state = deepcopy(state, memo)
261        if hasattr(y, '__setstate__'):
262            y.__setstate__(state)
263        else:
264            if isinstance(state, tuple) and len(state) == 2:
265                state, slotstate = state
266            else:
267                slotstate = None
268            if state is not None:
269                y.__dict__.update(state)
270            if slotstate is not None:
271                for key, value in slotstate.items():
272                    setattr(y, key, value)
273
274    if listiter is not None:
275        if deep:
276            for item in listiter:
277                item = deepcopy(item, memo)
278                y.append(item)
279        else:
280            for item in listiter:
281                y.append(item)
282    if dictiter is not None:
283        if deep:
284            for key, value in dictiter:
285                key = deepcopy(key, memo)
286                value = deepcopy(value, memo)
287                y[key] = value
288        else:
289            for key, value in dictiter:
290                y[key] = value
291    return y
292
293del types, weakref
294
295
296def replace(obj, /, **changes):
297    """Return a new object replacing specified fields with new values.
298
299    This is especially useful for immutable objects, like named tuples or
300    frozen dataclasses.
301    """
302    cls = obj.__class__
303    func = getattr(cls, '__replace__', None)
304    if func is None:
305        raise TypeError(f"replace() does not support {cls.__name__} objects")
306    return func(obj, **changes)
307