bpo-27141: Fix collections.UserList and UserDict shallow copy. (GH-4094) · python/cpython@3645d29 (original) (raw)

3 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -1036,6 +1036,13 @@ def __contains__(self, key):
1036 1036
1037 1037 # Now, add the methods in dicts but not in MutableMapping
1038 1038 def __repr__(self): return repr(self.data)
1039 +def __copy__(self):
1040 +inst = self.__class__.__new__(self.__class__)
1041 +inst.__dict__.update(self.__dict__)
1042 +# Create a copy and avoid triggering descriptors
1043 +inst.__dict__["data"] = self.__dict__["data"].copy()
1044 +return inst
1045 +
1039 1046 def copy(self):
1040 1047 if self.__class__ is UserDict:
1041 1048 return UserDict(self.data.copy())
@@ -1048,6 +1055,7 @@ def copy(self):
1048 1055 self.data = data
1049 1056 c.update(self)
1050 1057 return c
1058 +
1051 1059 @classmethod
1052 1060 def fromkeys(cls, iterable, value=None):
1053 1061 d = cls()
@@ -1112,6 +1120,12 @@ def __mul__(self, n):
1112 1120 def __imul__(self, n):
1113 1121 self.data *= n
1114 1122 return self
1123 +def __copy__(self):
1124 +inst = self.__class__.__new__(self.__class__)
1125 +inst.__dict__.update(self.__dict__)
1126 +# Create a copy and avoid triggering descriptors
1127 +inst.__dict__["data"] = self.__dict__["data"][:]
1128 +return inst
1115 1129 def append(self, item): self.data.append(item)
1116 1130 def insert(self, i, item): self.data.insert(i, item)
1117 1131 def pop(self, i=-1): return self.data.pop(i)
Original file line number Diff line number Diff line change
@@ -38,6 +38,20 @@ def _superset_test(self, a, b):
38 38 b=b.__name__,
39 39 ),
40 40 )
41 +
42 +def _copy_test(self, obj):
43 +# Test internal copy
44 +obj_copy = obj.copy()
45 +self.assertIsNot(obj.data, obj_copy.data)
46 +self.assertEqual(obj.data, obj_copy.data)
47 +
48 +# Test copy.copy
49 +obj.test = [1234] # Make sure instance vars are also copied.
50 +obj_copy = copy.copy(obj)
51 +self.assertIsNot(obj.data, obj_copy.data)
52 +self.assertEqual(obj.data, obj_copy.data)
53 +self.assertIs(obj.test, obj_copy.test)
54 +
41 55 def test_str_protocol(self):
42 56 self._superset_test(UserString, str)
43 57
@@ -47,6 +61,16 @@ def test_list_protocol(self):
47 61 def test_dict_protocol(self):
48 62 self._superset_test(UserDict, dict)
49 63
64 +def test_list_copy(self):
65 +obj = UserList()
66 +obj.append(123)
67 +self._copy_test(obj)
68 +
69 +def test_dict_copy(self):
70 +obj = UserDict()
71 +obj[123] = "abc"
72 +self._copy_test(obj)
73 +
50 74
51 75 ################################################################################
52 76 ### ChainMap (helper class for configparser and the string module)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 +Added a ``__copy__()`` to ``collections.UserList`` and
2 +``collections.UserDict`` in order to correctly implement shallow copying of
3 +the objects. Patch by Bar Harel.