diff --git a/Lib/copy.py b/Lib/copy.py index 41873f2c04..1b276afe08 100644 --- a/Lib/copy.py +++ b/Lib/copy.py @@ -39,8 +39,8 @@ class instances). set of components copied This version does not copy types like module, class, function, method, -nor stack trace, stack frame, nor file, socket, window, nor array, nor -any similar types. +nor stack trace, stack frame, nor file, socket, window, nor any +similar types. Classes can use the same interfaces to control copying that they use to control pickling: they can define methods called __getinitargs__(), @@ -192,6 +192,7 @@ def _deepcopy_atomic(x, memo): d[str] = _deepcopy_atomic d[types.CodeType] = _deepcopy_atomic d[type] = _deepcopy_atomic +d[range] = _deepcopy_atomic d[types.BuiltinFunctionType] = _deepcopy_atomic d[types.FunctionType] = _deepcopy_atomic d[weakref.ref] = _deepcopy_atomic @@ -257,7 +258,7 @@ def _keep_alive(x, memo): def _reconstruct(x, memo, func, args, state=None, listiter=None, dictiter=None, - deepcopy=deepcopy): + *, deepcopy=deepcopy): deep = memo is not None if deep and args: args = (deepcopy(arg, memo) for arg in args) diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 5622f78d17..913cf3c8bc 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -51,6 +51,9 @@ def pickle_C(obj): self.assertRaises(TypeError, copy.copy, x) copyreg.pickle(C, pickle_C, C) y = copy.copy(x) + self.assertIsNot(x, y) + self.assertEqual(type(y), C) + self.assertEqual(y.foo, x.foo) def test_copy_reduce_ex(self): class C(object): @@ -315,6 +318,9 @@ def pickle_C(obj): self.assertRaises(TypeError, copy.deepcopy, x) copyreg.pickle(C, pickle_C, C) y = copy.deepcopy(x) + self.assertIsNot(x, y) + self.assertEqual(type(y), C) + self.assertEqual(y.foo, x.foo) def test_deepcopy_reduce_ex(self): class C(object): @@ -351,8 +357,6 @@ def __getattribute__(self, name): # Type-specific _deepcopy_xxx() methods - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_deepcopy_atomic(self): class Classic: pass @@ -360,8 +364,8 @@ class NewStyle(object): pass def f(): pass - tests = [None, 42, 2**100, 3.14, True, False, 1j, - "hello", "hello\u1234", f.__code__, + tests = [None, ..., NotImplemented, 42, 2**100, 3.14, True, False, 1j, + b"bytes", "hello", "hello\u1234", f.__code__, NewStyle, range(10), Classic, max, property()] for x in tests: self.assertIs(copy.deepcopy(x), x) @@ -684,6 +688,28 @@ def __eq__(self, other): self.assertIsNot(x, y) self.assertIsNot(x["foo"], y["foo"]) + def test_reduce_6tuple(self): + def state_setter(*args, **kwargs): + self.fail("shouldn't call this") + class C: + def __reduce__(self): + return C, (), self.__dict__, None, None, state_setter + x = C() + with self.assertRaises(TypeError): + copy.copy(x) + with self.assertRaises(TypeError): + copy.deepcopy(x) + + def test_reduce_6tuple_none(self): + class C: + def __reduce__(self): + return C, (), self.__dict__, None, None, None + x = C() + with self.assertRaises(TypeError): + copy.copy(x) + with self.assertRaises(TypeError): + copy.deepcopy(x) + def test_copy_slots(self): class C(object): __slots__ = ["foo"]