Commit 5ea977e0 authored by Guido van Rossum's avatar Guido van Rossum

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.
parent 0ead8092
...@@ -48,10 +48,8 @@ __getstate__() and __setstate__(). See the documentation for module ...@@ -48,10 +48,8 @@ __getstate__() and __setstate__(). See the documentation for module
"pickle" for information on these methods. "pickle" for information on these methods.
""" """
# XXX need to support copy_reg here too...
import types import types
from copy_reg import _better_reduce from copy_reg import _better_reduce, dispatch_table
class Error(Exception): class Error(Exception):
pass pass
...@@ -70,25 +68,25 @@ def copy(x): ...@@ -70,25 +68,25 @@ def copy(x):
See the module's __doc__ string for more info. See the module's __doc__ string for more info.
""" """
try: cls = type(x)
copierfunction = _copy_dispatch[type(x)]
except KeyError: copier = _copy_dispatch.get(cls)
try: if copier:
copier = x.__copy__ return copier(x)
except AttributeError:
try: copier = getattr(cls, "__copy__", None)
reductor = x.__class__.__reduce__ if copier:
if reductor == object.__reduce__: return copier(x)
reductor = _better_reduce
except AttributeError: reductor = dispatch_table.get(cls)
raise Error("un(shallow)copyable object of type %s" % type(x)) if not reductor:
else: reductor = getattr(cls, "__reduce__", None)
y = _reconstruct(x, reductor(x), 0) if reductor == object.__reduce__:
else: reductor = _better_reduce
y = copier() elif not reductor:
else: raise Error("un(shallow)copyable object of type %s" % cls)
y = copierfunction(x)
return y return _reconstruct(x, reductor(x), 0)
_copy_dispatch = d = {} _copy_dispatch = d = {}
...@@ -153,7 +151,7 @@ d[types.InstanceType] = _copy_inst ...@@ -153,7 +151,7 @@ d[types.InstanceType] = _copy_inst
del d del d
def deepcopy(x, memo = None): def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects. """Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info. See the module's __doc__ string for more info.
...@@ -161,35 +159,39 @@ def deepcopy(x, memo = None): ...@@ -161,35 +159,39 @@ def deepcopy(x, memo = None):
if memo is None: if memo is None:
memo = {} memo = {}
d = id(x) d = id(x)
if d in memo: y = memo.get(d, _nil)
return memo[d] if y is not _nil:
try: return y
copierfunction = _deepcopy_dispatch[type(x)]
except KeyError: cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier:
y = copier(x, memo)
else:
try: try:
issc = issubclass(type(x), type) issc = issubclass(cls, type)
except TypeError: except TypeError: # cls is not a class (old Boost; see SF #502085)
issc = 0 issc = 0
if issc: if issc:
y = _deepcopy_dispatch[type](x, memo) copier = _deepcopy_atomic
else: else:
try: copier = getattr(cls, "__deepcopy__", None)
copier = x.__deepcopy__
except AttributeError: if copier:
try: y = copier(x, memo)
reductor = x.__class__.__reduce__ else:
if reductor == object.__reduce__: reductor = dispatch_table.get(cls)
reductor = _better_reduce if not reductor:
except AttributeError: reductor = getattr(cls, "__reduce__", None)
raise Error("un(shallow)copyable object of type %s" % if reductor == object.__reduce__:
type(x)) reductor = _better_reduce
else: elif not reductor:
y = _reconstruct(x, reductor(x), 1, memo) raise Error("un(deep)copyable object of type %s" % cls)
else: y = _reconstruct(x, reductor(x), 1, memo)
y = copier(memo)
else:
y = copierfunction(x, memo)
memo[d] = y memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d _keep_alive(x, memo) # Make sure x lives at least as long as d
return y return y
...@@ -380,7 +382,7 @@ def _test(): ...@@ -380,7 +382,7 @@ def _test():
def __setstate__(self, state): def __setstate__(self, state):
for key, value in state.iteritems(): for key, value in state.iteritems():
setattr(self, key, value) setattr(self, key, value)
def __deepcopy__(self, memo = None): def __deepcopy__(self, memo=None):
new = self.__class__(deepcopy(self.arg, memo)) new = self.__class__(deepcopy(self.arg, memo))
new.a = self.a new.a = self.a
return new return new
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import sys import sys
import copy import copy
import copy_reg
import unittest import unittest
from test import test_support from test import test_support
...@@ -32,6 +33,19 @@ class TestCopy(unittest.TestCase): ...@@ -32,6 +33,19 @@ class TestCopy(unittest.TestCase):
self.assertEqual(y.__class__, x.__class__) self.assertEqual(y.__class__, x.__class__)
self.assertEqual(y.foo, x.foo) self.assertEqual(y.foo, x.foo)
def test_copy_registry(self):
class C(object):
def __new__(cls, foo):
obj = object.__new__(cls)
obj.foo = foo
return obj
def pickle_C(obj):
return (C, (obj.foo,))
x = C(42)
self.assertRaises(TypeError, copy.copy, x)
copy_reg.pickle(C, pickle_C, C)
y = copy.copy(x)
def test_copy_reduce(self): def test_copy_reduce(self):
class C(object): class C(object):
def __reduce__(self): def __reduce__(self):
...@@ -182,6 +196,19 @@ class TestCopy(unittest.TestCase): ...@@ -182,6 +196,19 @@ class TestCopy(unittest.TestCase):
self.assertEqual(y.__class__, x.__class__) self.assertEqual(y.__class__, x.__class__)
self.assertEqual(y.foo, x.foo) self.assertEqual(y.foo, x.foo)
def test_deepcopy_registry(self):
class C(object):
def __new__(cls, foo):
obj = object.__new__(cls)
obj.foo = foo
return obj
def pickle_C(obj):
return (C, (obj.foo,))
x = C(42)
self.assertRaises(TypeError, copy.deepcopy, x)
copy_reg.pickle(C, pickle_C, C)
y = copy.deepcopy(x)
def test_deepcopy_reduce(self): def test_deepcopy_reduce(self):
class C(object): class C(object):
def __reduce__(self): def __reduce__(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment