diff -r 775b74e0e103 Lib/copy.py --- a/Lib/copy.py Wed Jan 20 12:16:21 2016 +0100 +++ b/Lib/copy.py Wed Jan 20 22:10:55 2016 +0200 @@ -51,7 +51,6 @@ to control pickling: they can define met import types import weakref from copyreg import dispatch_table -import builtins class Error(Exception): pass @@ -102,37 +101,33 @@ def copy(x): else: raise Error("un(shallow)copyable object of type %s" % cls) - return _reconstruct(x, rv, 0) + if isinstance(rv, str): + return x + return _reconstruct(x, None, *rv) _copy_dispatch = d = {} def _copy_immutable(x): return x -for t in (type(None), int, float, bool, str, tuple, - bytes, frozenset, type, range, - types.BuiltinFunctionType, type(Ellipsis), +for t in (type(None), int, float, bool, complex, str, tuple, + bytes, frozenset, type, range, slice, + types.BuiltinFunctionType, type(Ellipsis), type(NotImplemented), types.FunctionType, weakref.ref): d[t] = _copy_immutable t = getattr(types, "CodeType", None) if t is not None: d[t] = _copy_immutable -for name in ("complex", "unicode"): - t = getattr(builtins, name, None) - if t is not None: - d[t] = _copy_immutable -def _copy_with_constructor(x): - return type(x)(x) -for t in (list, dict, set): - d[t] = _copy_with_constructor +d[list] = list.copy +d[dict] = dict.copy +d[set] = set.copy +d[bytearray] = bytearray.copy -def _copy_with_copy_method(x): - return x.copy() if PyStringMap is not None: - d[PyStringMap] = _copy_with_copy_method + d[PyStringMap] = PyStringMap.copy -del d +del d, t def deepcopy(x, memo=None, _nil=[]): """Deep copy operation on arbitrary Python objects. @@ -179,7 +174,10 @@ def deepcopy(x, memo=None, _nil=[]): else: raise Error( "un(deep)copyable object of type %s" % cls) - y = _reconstruct(x, rv, 1, memo) + if isinstance(rv, str): + y = x + else: + y = _reconstruct(x, memo, *rv) # If is its own copy, don't memoize. if y is not x: @@ -193,13 +191,11 @@ def _deepcopy_atomic(x, memo): return x d[type(None)] = _deepcopy_atomic d[type(Ellipsis)] = _deepcopy_atomic +d[type(NotImplemented)] = _deepcopy_atomic d[int] = _deepcopy_atomic d[float] = _deepcopy_atomic d[bool] = _deepcopy_atomic -try: - d[complex] = _deepcopy_atomic -except NameError: - pass +d[complex] = _deepcopy_atomic d[bytes] = _deepcopy_atomic d[str] = _deepcopy_atomic try: @@ -212,15 +208,16 @@ d[types.BuiltinFunctionType] = _deepcopy d[types.FunctionType] = _deepcopy_atomic d[weakref.ref] = _deepcopy_atomic -def _deepcopy_list(x, memo): +def _deepcopy_list(x, memo, deepcopy=deepcopy): y = [] memo[id(x)] = y + append = y.append for a in x: - y.append(deepcopy(a, memo)) + append(deepcopy(a, memo)) return y d[list] = _deepcopy_list -def _deepcopy_tuple(x, memo): +def _deepcopy_tuple(x, memo, deepcopy=deepcopy): y = [deepcopy(a, memo) for a in x] # We're not going to put the tuple in the memo, but it's still important we # check for it, in case the tuple contains recursive mutable structures. @@ -237,7 +234,7 @@ def _deepcopy_tuple(x, memo): return y d[tuple] = _deepcopy_tuple -def _deepcopy_dict(x, memo): +def _deepcopy_dict(x, memo, deepcopy=deepcopy): y = {} memo[id(x)] = y for key, value in x.items(): @@ -249,7 +246,9 @@ if PyStringMap is not None: def _deepcopy_method(x, memo): # Copy instance methods return type(x)(x.__func__, deepcopy(x.__self__, memo)) -_deepcopy_dispatch[types.MethodType] = _deepcopy_method +d[types.MethodType] = _deepcopy_method + +del d def _keep_alive(x, memo): """Keeps a reference to the object x in the memo. @@ -267,31 +266,15 @@ def _keep_alive(x, memo): # aha, this is the first one :-) memo[id(memo)]=[x] -def _reconstruct(x, info, deep, memo=None): - if isinstance(info, str): - return x - assert isinstance(info, tuple) - if memo is None: - memo = {} - n = len(info) - assert n in (2, 3, 4, 5) - callable, args = info[:2] - if n > 2: - state = info[2] - else: - state = None - if n > 3: - listiter = info[3] - else: - listiter = None - if n > 4: - dictiter = info[4] - else: - dictiter = None +def _reconstruct(x, memo, func, args, + state=None, listiter=None, dictiter=None, + deepcopy=deepcopy): + deep = memo is not None + if deep and args: + args = (deepcopy(arg, memo) for arg in args) + y = func(*args) if deep: - args = deepcopy(args, memo) - y = callable(*args) - memo[id(x)] = y + memo[id(x)] = y if state is not None: if deep: @@ -310,22 +293,22 @@ def _reconstruct(x, info, deep, memo=Non setattr(y, key, value) if listiter is not None: - for item in listiter: - if deep: + if deep: + for item in listiter: item = deepcopy(item, memo) - y.append(item) + y.append(item) + else: + for item in listiter: + y.append(item) if dictiter is not None: - for key, value in dictiter: - if deep: + if deep: + for key, value in dictiter: key = deepcopy(key, memo) value = deepcopy(value, memo) - y[key] = value + y[key] = value + else: + for key, value in dictiter: + y[key] = value return y -del d - -del types - -# Helper for instance creation without calling __init__ -class _EmptyClass: - pass +del types, weakref, PyStringMap diff -r 775b74e0e103 Lib/test/test_copy.py --- a/Lib/test/test_copy.py Wed Jan 20 12:16:21 2016 +0100 +++ b/Lib/test/test_copy.py Wed Jan 20 22:10:55 2016 +0200 @@ -95,24 +95,67 @@ class TestCopy(unittest.TestCase): pass class WithMetaclass(metaclass=abc.ABCMeta): pass - tests = [None, 42, 2**100, 3.14, True, False, 1j, + tests = [None, ..., NotImplemented, + 42, 2**100, 3.14, True, False, 1j, "hello", "hello\u1234", f.__code__, - b"world", bytes(range(256)), - NewStyle, range(10), Classic, max, WithMetaclass] + b"world", bytes(range(256)), range(10), slice(1, 10, 2), + NewStyle, Classic, max, WithMetaclass] for x in tests: self.assertIs(copy.copy(x), x) def test_copy_list(self): x = [1, 2, 3] - self.assertEqual(copy.copy(x), x) + y = copy.copy(x) + self.assertEqual(y, x) + self.assertIsNot(y, x) + x = [] + y = copy.copy(x) + self.assertEqual(y, x) + self.assertIsNot(y, x) def test_copy_tuple(self): x = (1, 2, 3) - self.assertEqual(copy.copy(x), x) + self.assertIs(copy.copy(x), x) + x = () + self.assertIs(copy.copy(x), x) + x = (1, 2, 3, []) + self.assertIs(copy.copy(x), x) def test_copy_dict(self): x = {"foo": 1, "bar": 2} - self.assertEqual(copy.copy(x), x) + y = copy.copy(x) + self.assertEqual(y, x) + self.assertIsNot(y, x) + x = {} + y = copy.copy(x) + self.assertEqual(y, x) + self.assertIsNot(y, x) + + def test_copy_set(self): + x = {1, 2, 3} + y = copy.copy(x) + self.assertEqual(y, x) + self.assertIsNot(y, x) + x = set() + y = copy.copy(x) + self.assertEqual(y, x) + self.assertIsNot(y, x) + + def test_copy_frozenset(self): + x = frozenset({1, 2, 3}) + self.assertIs(copy.copy(x), x) + x = frozenset() + self.assertIs(copy.copy(x), x) + + def test_copy_bytearray(self): + x = bytearray(b'abc') + y = copy.copy(x) + self.assertEqual(y, x) + self.assertIsNot(y, x) + x = bytearray() + y = copy.copy(x) + self.assertEqual(y, x) + self.assertIsNot(y, x) def test_copy_inst_vanilla(self): class C: