]> granicus.if.org Git - python/commitdiff
Changes to copy() and deepcopy() in copy.py to support __reduce__ as a
authorGuido van Rossum <guido@python.org>
Fri, 28 Sep 2001 18:13:29 +0000 (18:13 +0000)
committerGuido van Rossum <guido@python.org>
Fri, 28 Sep 2001 18:13:29 +0000 (18:13 +0000)
fallback for objects that are neither supported by our dispatch table
nor have a __copy__ or __deepcopy__ method.

Changes to _reduce() in copy_reg.py to support reducing objects that
don't have a __dict__ -- copy.copy(complex()) now invokes _reduce().

Add tests for copy.copy() and copy.deepcopy() to test_regrtest.py.

Lib/copy.py
Lib/copy_reg.py
Lib/test/test_descr.py

index c8cc88092c66de3e38a36dc9aa8bce5c3e908590..e907738180c8dfa0bd4c01d9f7e98b6775d58bd8 100644 (file)
@@ -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
index eb028640fbc6d4660cbb220b7d3abd5097a70e48..d4697449a022bb011d93dcc3eade06a1409a6f69 100644 (file)
@@ -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
index 0357a065be14ec984318eb9c4c75c5c70da25eb9..2c5e7a4008c2f8fac60efd86972813a3421c432e 100644 (file)
@@ -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__":