Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c06e3ac

Browse files
committed
Add support for copy_reg.dispatch_table.
Rewrote copy() and deepcopy() without avoidable try/except statements; getattr(x, name, None) or dict.get() are much faster than try/except.
1 parent 2731c5c commit c06e3ac

2 files changed

Lines changed: 77 additions & 48 deletions

File tree

Lib/copy.py

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ class instances).
4848
"pickle" for information on these methods.
4949
"""
5050

51-
# XXX need to support copy_reg here too...
52-
5351
import types
54-
from copy_reg import _better_reduce
52+
from copy_reg import _better_reduce, dispatch_table
5553

5654
class Error(Exception):
5755
pass
@@ -70,25 +68,25 @@ def copy(x):
7068
See the module's __doc__ string for more info.
7169
"""
7270

73-
try:
74-
copierfunction = _copy_dispatch[type(x)]
75-
except KeyError:
76-
try:
77-
copier = x.__copy__
78-
except AttributeError:
79-
try:
80-
reductor = x.__class__.__reduce__
81-
if reductor == object.__reduce__:
82-
reductor = _better_reduce
83-
except AttributeError:
84-
raise Error("un(shallow)copyable object of type %s" % type(x))
85-
else:
86-
y = _reconstruct(x, reductor(x), 0)
87-
else:
88-
y = copier()
89-
else:
90-
y = copierfunction(x)
91-
return y
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 not reductor:
83+
reductor = getattr(cls, "__reduce__", None)
84+
if reductor == object.__reduce__:
85+
reductor = _better_reduce
86+
elif not reductor:
87+
raise Error("un(shallow)copyable object of type %s" % cls)
88+
89+
return _reconstruct(x, reductor(x), 0)
9290

9391

9492
_copy_dispatch = d = {}
@@ -153,43 +151,47 @@ def _copy_inst(x):
153151

154152
del d
155153

156-
def deepcopy(x, memo = None):
154+
def deepcopy(x, memo=None, _nil=[]):
157155
"""Deep copy operation on arbitrary Python objects.
158156
159157
See the module's __doc__ string for more info.
160158
"""
161159

162160
if memo is None:
163161
memo = {}
162+
164163
d = id(x)
165-
if d in memo:
166-
return memo[d]
167-
try:
168-
copierfunction = _deepcopy_dispatch[type(x)]
169-
except KeyError:
164+
y = memo.get(d, _nil)
165+
if y is not _nil:
166+
return y
167+
168+
cls = type(x)
169+
170+
copier = _deepcopy_dispatch.get(cls)
171+
if copier:
172+
y = copier(x, memo)
173+
else:
170174
try:
171-
issc = issubclass(type(x), type)
172-
except TypeError:
175+
issc = issubclass(cls, type)
176+
except TypeError: # cls is not a class (old Boost; see SF #502085)
173177
issc = 0
174178
if issc:
175-
y = _deepcopy_dispatch[type](x, memo)
179+
copier = _deepcopy_atomic
176180
else:
177-
try:
178-
copier = x.__deepcopy__
179-
except AttributeError:
180-
try:
181-
reductor = x.__class__.__reduce__
182-
if reductor == object.__reduce__:
183-
reductor = _better_reduce
184-
except AttributeError:
185-
raise Error("un(shallow)copyable object of type %s" %
186-
type(x))
187-
else:
188-
y = _reconstruct(x, reductor(x), 1, memo)
189-
else:
190-
y = copier(memo)
191-
else:
192-
y = copierfunction(x, memo)
181+
copier = getattr(cls, "__deepcopy__", None)
182+
183+
if copier:
184+
y = copier(x, memo)
185+
else:
186+
reductor = dispatch_table.get(cls)
187+
if not reductor:
188+
reductor = getattr(cls, "__reduce__", None)
189+
if reductor == object.__reduce__:
190+
reductor = _better_reduce
191+
elif not reductor:
192+
raise Error("un(deep)copyable object of type %s" % cls)
193+
y = _reconstruct(x, reductor(x), 1, memo)
194+
193195
memo[d] = y
194196
_keep_alive(x, memo) # Make sure x lives at least as long as d
195197
return y
@@ -380,7 +382,7 @@ def __getstate__(self):
380382
def __setstate__(self, state):
381383
for key, value in state.iteritems():
382384
setattr(self, key, value)
383-
def __deepcopy__(self, memo = None):
385+
def __deepcopy__(self, memo=None):
384386
new = self.__class__(deepcopy(self.arg, memo))
385387
new.a = self.a
386388
return new

Lib/test/test_copy.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import sys
44
import copy
5+
import copy_reg
56

67
import unittest
78
from test import test_support
@@ -32,6 +33,19 @@ def __copy__(self):
3233
self.assertEqual(y.__class__, x.__class__)
3334
self.assertEqual(y.foo, x.foo)
3435

36+
def test_copy_registry(self):
37+
class C(object):
38+
def __new__(cls, foo):
39+
obj = object.__new__(cls)
40+
obj.foo = foo
41+
return obj
42+
def pickle_C(obj):
43+
return (C, (obj.foo,))
44+
x = C(42)
45+
self.assertRaises(TypeError, copy.copy, x)
46+
copy_reg.pickle(C, pickle_C, C)
47+
y = copy.copy(x)
48+
3549
def test_copy_reduce(self):
3650
class C(object):
3751
def __reduce__(self):
@@ -182,6 +196,19 @@ def __deepcopy__(self, memo=None):
182196
self.assertEqual(y.__class__, x.__class__)
183197
self.assertEqual(y.foo, x.foo)
184198

199+
def test_deepcopy_registry(self):
200+
class C(object):
201+
def __new__(cls, foo):
202+
obj = object.__new__(cls)
203+
obj.foo = foo
204+
return obj
205+
def pickle_C(obj):
206+
return (C, (obj.foo,))
207+
x = C(42)
208+
self.assertRaises(TypeError, copy.deepcopy, x)
209+
copy_reg.pickle(C, pickle_C, C)
210+
y = copy.deepcopy(x)
211+
185212
def test_deepcopy_reduce(self):
186213
class C(object):
187214
def __reduce__(self):

0 commit comments

Comments
 (0)