summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/copy.py46
-rw-r--r--Lib/copy_reg.py10
-rw-r--r--Lib/test/test_descr.py44
3 files changed, 91 insertions, 9 deletions
diff --git a/Lib/copy.py b/Lib/copy.py
index c8cc880..e907738 100644
--- a/Lib/copy.py
+++ b/Lib/copy.py
@@ -61,7 +61,7 @@ try:
except ImportError:
PyStringMap = None
-__all__ = ["Error","error","copy","deepcopy"]
+__all__ = ["Error", "error", "copy", "deepcopy"]
def copy(x):
"""Shallow copy operation on arbitrary Python objects.
@@ -75,9 +75,15 @@ def copy(x):
try:
copier = x.__copy__
except AttributeError:
- raise error, \
- "un(shallow)copyable object of type %s" % type(x)
- y = copier()
+ try:
+ reductor = x.__reduce__
+ except AttributeError:
+ raise error, \
+ "un(shallow)copyable object of type %s" % type(x)
+ else:
+ y = _reconstruct(x, reductor(), 0)
+ else:
+ y = copier()
else:
y = copierfunction(x)
return y
@@ -156,9 +162,15 @@ def deepcopy(x, memo = None):
try:
copier = x.__deepcopy__
except AttributeError:
- raise error, \
- "un-deep-copyable object of type %s" % type(x)
- y = copier(memo)
+ try:
+ reductor = x.__reduce__
+ except AttributeError:
+ raise error, \
+ "un-deep-copyable object of type %s" % type(x)
+ else:
+ y = _reconstruct(x, reductor(), 1)
+ else:
+ y = copier(memo)
else:
y = copierfunction(x, memo)
memo[d] = y
@@ -259,6 +271,26 @@ def _deepcopy_inst(x, memo):
return y
d[types.InstanceType] = _deepcopy_inst
+def _reconstruct(x, info, deep):
+ if isinstance(info, str):
+ return x
+ assert isinstance(info, tuple)
+ n = len(info)
+ assert n in (2, 3)
+ callable, args = info[:2]
+ if n > 2:
+ state = info[2]
+ else:
+ state = {}
+ if deep:
+ args = deepcopy(args)
+ y = callable(*args)
+ if state:
+ if deep:
+ state = deepcopy(state)
+ y.__dict__.update(state)
+ return y
+
del d
del types
diff --git a/Lib/copy_reg.py b/Lib/copy_reg.py
index eb02864..d469744 100644
--- a/Lib/copy_reg.py
+++ b/Lib/copy_reg.py
@@ -54,4 +54,12 @@ def _reduce(self):
state = None
else:
state = base(self)
- return _reconstructor, (self.__class__, base, state), self.__dict__
+ args = (self.__class__, base, state)
+ try:
+ dict = self.__dict__
+ except AttributeError:
+ dict = None
+ if dict:
+ return _reconstructor, args, dict
+ else:
+ return _reconstructor, args
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 0357a06..2c5e7a4 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -2033,7 +2033,8 @@ def setclass():
cant(list(), object)
def pickles():
- if verbose: print "Testing pickling new-style classes and objects..."
+ if verbose:
+ print "Testing pickling and copying new-style classes and objects..."
import pickle, cPickle
def sorteditems(d):
@@ -2092,6 +2093,46 @@ def pickles():
print "a = x =", a
print "b = y =", b
+ # Testing copy.deepcopy()
+ import copy
+ for cls in C, C1, C2:
+ cls2 = copy.deepcopy(cls)
+ verify(cls2 is cls)
+
+ a = C1(1, 2); a.append(42); a.append(24)
+ b = C2("hello", "world", 42)
+ x, y = copy.deepcopy((a, b))
+ assert x.__class__ == a.__class__
+ assert sorteditems(x.__dict__) == sorteditems(a.__dict__)
+ assert y.__class__ == b.__class__
+ assert sorteditems(y.__dict__) == sorteditems(b.__dict__)
+ assert `x` == `a`
+ assert `y` == `b`
+ if verbose:
+ print "a = x =", a
+ print "b = y =", b
+
+def copies():
+ if verbose: print "Testing copy.copy() and copy.deepcopy()..."
+ import copy
+ class C(object):
+ pass
+
+ a = C()
+ a.foo = 12
+ b = copy.copy(a)
+ verify(b.__dict__ == a.__dict__)
+
+ a.bar = [1,2,3]
+ c = copy.copy(a)
+ verify(c.bar == a.bar)
+ verify(c.bar is a.bar)
+
+ d = copy.deepcopy(a)
+ verify(d.__dict__ == a.__dict__)
+ a.bar.append(4)
+ verify(d.bar == [1,2,3])
+
def test_main():
lists()
@@ -2136,6 +2177,7 @@ def test_main():
descrdoc()
setclass()
pickles()
+ copies()
if verbose: print "All OK"
if __name__ == "__main__":