diff options
-rw-r--r-- | Lib/copy.py | 46 | ||||
-rw-r--r-- | Lib/copy_reg.py | 10 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 44 |
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__": |