Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/dns/set.py: 31%

149 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-02 06:07 +0000

1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license 

2 

3# Copyright (C) 2003-2017 Nominum, Inc. 

4# 

5# Permission to use, copy, modify, and distribute this software and its 

6# documentation for any purpose with or without fee is hereby granted, 

7# provided that the above copyright notice and this permission notice 

8# appear in all copies. 

9# 

10# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES 

11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 

12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR 

13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 

14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 

15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 

16# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 

17 

18import itertools 

19 

20 

21class Set: 

22 

23 """A simple set class. 

24 

25 This class was originally used to deal with sets being missing in 

26 ancient versions of python, but dnspython will continue to use it 

27 as these sets are based on lists and are thus indexable, and this 

28 ability is widely used in dnspython applications. 

29 """ 

30 

31 __slots__ = ["items"] 

32 

33 def __init__(self, items=None): 

34 """Initialize the set. 

35 

36 *items*, an iterable or ``None``, the initial set of items. 

37 """ 

38 

39 self.items = dict() 

40 if items is not None: 

41 for item in items: 

42 # This is safe for how we use set, but if other code 

43 # subclasses it could be a legitimate issue. 

44 self.add(item) # lgtm[py/init-calls-subclass] 

45 

46 def __repr__(self): 

47 return "dns.set.Set(%s)" % repr(list(self.items.keys())) 

48 

49 def add(self, item): 

50 """Add an item to the set.""" 

51 

52 if item not in self.items: 

53 self.items[item] = None 

54 

55 def remove(self, item): 

56 """Remove an item from the set.""" 

57 

58 try: 

59 del self.items[item] 

60 except KeyError: 

61 raise ValueError 

62 

63 def discard(self, item): 

64 """Remove an item from the set if present.""" 

65 

66 self.items.pop(item, None) 

67 

68 def pop(self): 

69 """Remove an arbitrary item from the set.""" 

70 (k, _) = self.items.popitem() 

71 return k 

72 

73 def _clone(self) -> "Set": 

74 """Make a (shallow) copy of the set. 

75 

76 There is a 'clone protocol' that subclasses of this class 

77 should use. To make a copy, first call your super's _clone() 

78 method, and use the object returned as the new instance. Then 

79 make shallow copies of the attributes defined in the subclass. 

80 

81 This protocol allows us to write the set algorithms that 

82 return new instances (e.g. union) once, and keep using them in 

83 subclasses. 

84 """ 

85 

86 if hasattr(self, "_clone_class"): 

87 cls = self._clone_class # type: ignore 

88 else: 

89 cls = self.__class__ 

90 obj = cls.__new__(cls) 

91 obj.items = dict() 

92 obj.items.update(self.items) 

93 return obj 

94 

95 def __copy__(self): 

96 """Make a (shallow) copy of the set.""" 

97 

98 return self._clone() 

99 

100 def copy(self): 

101 """Make a (shallow) copy of the set.""" 

102 

103 return self._clone() 

104 

105 def union_update(self, other): 

106 """Update the set, adding any elements from other which are not 

107 already in the set. 

108 """ 

109 

110 if not isinstance(other, Set): 

111 raise ValueError("other must be a Set instance") 

112 if self is other: # lgtm[py/comparison-using-is] 

113 return 

114 for item in other.items: 

115 self.add(item) 

116 

117 def intersection_update(self, other): 

118 """Update the set, removing any elements from other which are not 

119 in both sets. 

120 """ 

121 

122 if not isinstance(other, Set): 

123 raise ValueError("other must be a Set instance") 

124 if self is other: # lgtm[py/comparison-using-is] 

125 return 

126 # we make a copy of the list so that we can remove items from 

127 # the list without breaking the iterator. 

128 for item in list(self.items): 

129 if item not in other.items: 

130 del self.items[item] 

131 

132 def difference_update(self, other): 

133 """Update the set, removing any elements from other which are in 

134 the set. 

135 """ 

136 

137 if not isinstance(other, Set): 

138 raise ValueError("other must be a Set instance") 

139 if self is other: # lgtm[py/comparison-using-is] 

140 self.items.clear() 

141 else: 

142 for item in other.items: 

143 self.discard(item) 

144 

145 def symmetric_difference_update(self, other): 

146 """Update the set, retaining only elements unique to both sets.""" 

147 

148 if not isinstance(other, Set): 

149 raise ValueError("other must be a Set instance") 

150 if self is other: # lgtm[py/comparison-using-is] 

151 self.items.clear() 

152 else: 

153 overlap = self.intersection(other) 

154 self.union_update(other) 

155 self.difference_update(overlap) 

156 

157 def union(self, other): 

158 """Return a new set which is the union of ``self`` and ``other``. 

159 

160 Returns the same Set type as this set. 

161 """ 

162 

163 obj = self._clone() 

164 obj.union_update(other) 

165 return obj 

166 

167 def intersection(self, other): 

168 """Return a new set which is the intersection of ``self`` and 

169 ``other``. 

170 

171 Returns the same Set type as this set. 

172 """ 

173 

174 obj = self._clone() 

175 obj.intersection_update(other) 

176 return obj 

177 

178 def difference(self, other): 

179 """Return a new set which ``self`` - ``other``, i.e. the items 

180 in ``self`` which are not also in ``other``. 

181 

182 Returns the same Set type as this set. 

183 """ 

184 

185 obj = self._clone() 

186 obj.difference_update(other) 

187 return obj 

188 

189 def symmetric_difference(self, other): 

190 """Return a new set which (``self`` - ``other``) | (``other`` 

191 - ``self), ie: the items in either ``self`` or ``other`` which 

192 are not contained in their intersection. 

193 

194 Returns the same Set type as this set. 

195 """ 

196 

197 obj = self._clone() 

198 obj.symmetric_difference_update(other) 

199 return obj 

200 

201 def __or__(self, other): 

202 return self.union(other) 

203 

204 def __and__(self, other): 

205 return self.intersection(other) 

206 

207 def __add__(self, other): 

208 return self.union(other) 

209 

210 def __sub__(self, other): 

211 return self.difference(other) 

212 

213 def __xor__(self, other): 

214 return self.symmetric_difference(other) 

215 

216 def __ior__(self, other): 

217 self.union_update(other) 

218 return self 

219 

220 def __iand__(self, other): 

221 self.intersection_update(other) 

222 return self 

223 

224 def __iadd__(self, other): 

225 self.union_update(other) 

226 return self 

227 

228 def __isub__(self, other): 

229 self.difference_update(other) 

230 return self 

231 

232 def __ixor__(self, other): 

233 self.symmetric_difference_update(other) 

234 return self 

235 

236 def update(self, other): 

237 """Update the set, adding any elements from other which are not 

238 already in the set. 

239 

240 *other*, the collection of items with which to update the set, which 

241 may be any iterable type. 

242 """ 

243 

244 for item in other: 

245 self.add(item) 

246 

247 def clear(self): 

248 """Make the set empty.""" 

249 self.items.clear() 

250 

251 def __eq__(self, other): 

252 return self.items == other.items 

253 

254 def __ne__(self, other): 

255 return not self.__eq__(other) 

256 

257 def __len__(self): 

258 return len(self.items) 

259 

260 def __iter__(self): 

261 return iter(self.items) 

262 

263 def __getitem__(self, i): 

264 if isinstance(i, slice): 

265 return list(itertools.islice(self.items, i.start, i.stop, i.step)) 

266 else: 

267 return next(itertools.islice(self.items, i, i + 1)) 

268 

269 def __delitem__(self, i): 

270 if isinstance(i, slice): 

271 for elt in list(self[i]): 

272 del self.items[elt] 

273 else: 

274 del self.items[self[i]] 

275 

276 def issubset(self, other): 

277 """Is this set a subset of *other*? 

278 

279 Returns a ``bool``. 

280 """ 

281 

282 if not isinstance(other, Set): 

283 raise ValueError("other must be a Set instance") 

284 for item in self.items: 

285 if item not in other.items: 

286 return False 

287 return True 

288 

289 def issuperset(self, other): 

290 """Is this set a superset of *other*? 

291 

292 Returns a ``bool``. 

293 """ 

294 

295 if not isinstance(other, Set): 

296 raise ValueError("other must be a Set instance") 

297 for item in other.items: 

298 if item not in self.items: 

299 return False 

300 return True 

301 

302 def isdisjoint(self, other): 

303 if not isinstance(other, Set): 

304 raise ValueError("other must be a Set instance") 

305 for item in other.items: 

306 if item in self.items: 

307 return False 

308 return True