blob: 148a9bdc35ee3e58e798b0cb819f51b154db6898 [file] [log] [blame]
Victor Stinner742da042016-09-07 17:40:12 -07001import builtins
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +02002import contextlib
3import copy
Serhiy Storchakad205d012016-01-19 14:46:25 +02004import gc
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +02005import pickle
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +02006from random import randrange, shuffle
Serhiy Storchaka0ce7a3a2015-12-22 08:16:18 +02007import struct
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +02008import sys
9import unittest
Serhiy Storchakad205d012016-01-19 14:46:25 +020010import weakref
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +020011from collections.abc import MutableMapping
12from test import mapping_tests, support
13
14
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +020015py_coll = support.import_fresh_module('collections', blocked=['_collections'])
16c_coll = support.import_fresh_module('collections', fresh=['_collections'])
17
18
19@contextlib.contextmanager
20def replaced_module(name, replacement):
21 original_module = sys.modules[name]
22 sys.modules[name] = replacement
23 try:
24 yield
25 finally:
26 sys.modules[name] = original_module
27
28
29class OrderedDictTests:
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +020030
31 def test_init(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +020032 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +020033 with self.assertRaises(TypeError):
34 OrderedDict([('a', 1), ('b', 2)], None) # too many args
35 pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
36 self.assertEqual(sorted(OrderedDict(dict(pairs)).items()), pairs) # dict input
37 self.assertEqual(sorted(OrderedDict(**dict(pairs)).items()), pairs) # kwds input
38 self.assertEqual(list(OrderedDict(pairs).items()), pairs) # pairs input
39 self.assertEqual(list(OrderedDict([('a', 1), ('b', 2), ('c', 9), ('d', 4)],
40 c=3, e=5).items()), pairs) # mixed input
41
42 # make sure no positional args conflict with possible kwdargs
43 self.assertEqual(list(OrderedDict(self=42).items()), [('self', 42)])
44 self.assertEqual(list(OrderedDict(other=42).items()), [('other', 42)])
45 self.assertRaises(TypeError, OrderedDict, 42)
46 self.assertRaises(TypeError, OrderedDict, (), ())
47 self.assertRaises(TypeError, OrderedDict.__init__)
48
49 # Make sure that direct calls to __init__ do not clear previous contents
50 d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
51 d.__init__([('e', 5), ('f', 6)], g=7, d=4)
52 self.assertEqual(list(d.items()),
53 [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
54
Raymond Hettingerec53b072017-01-08 00:37:13 -080055 def test_468(self):
56 OrderedDict = self.OrderedDict
57 items = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)]
58 shuffle(items)
59 argdict = OrderedDict(items)
60 d = OrderedDict(**argdict)
61 self.assertEqual(list(d.items()), items)
62
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +020063 def test_update(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +020064 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +020065 with self.assertRaises(TypeError):
66 OrderedDict().update([('a', 1), ('b', 2)], None) # too many args
67 pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
68 od = OrderedDict()
69 od.update(dict(pairs))
70 self.assertEqual(sorted(od.items()), pairs) # dict input
71 od = OrderedDict()
72 od.update(**dict(pairs))
73 self.assertEqual(sorted(od.items()), pairs) # kwds input
74 od = OrderedDict()
75 od.update(pairs)
76 self.assertEqual(list(od.items()), pairs) # pairs input
77 od = OrderedDict()
78 od.update([('a', 1), ('b', 2), ('c', 9), ('d', 4)], c=3, e=5)
79 self.assertEqual(list(od.items()), pairs) # mixed input
80
81 # Issue 9137: Named argument called 'other' or 'self'
82 # shouldn't be treated specially.
83 od = OrderedDict()
84 od.update(self=23)
85 self.assertEqual(list(od.items()), [('self', 23)])
86 od = OrderedDict()
87 od.update(other={})
88 self.assertEqual(list(od.items()), [('other', {})])
89 od = OrderedDict()
90 od.update(red=5, blue=6, other=7, self=8)
91 self.assertEqual(sorted(list(od.items())),
92 [('blue', 6), ('other', 7), ('red', 5), ('self', 8)])
93
94 # Make sure that direct calls to update do not clear previous contents
95 # add that updates items are not moved to the end
96 d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
97 d.update([('e', 5), ('f', 6)], g=7, d=4)
98 self.assertEqual(list(d.items()),
99 [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
100
101 self.assertRaises(TypeError, OrderedDict().update, 42)
102 self.assertRaises(TypeError, OrderedDict().update, (), ())
103 self.assertRaises(TypeError, OrderedDict.update)
104
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200105 self.assertRaises(TypeError, OrderedDict().update, 42)
106 self.assertRaises(TypeError, OrderedDict().update, (), ())
107 self.assertRaises(TypeError, OrderedDict.update)
108
Eric Snow06aed902016-09-09 11:59:08 -0700109 def test_init_calls(self):
110 calls = []
111 class Spam:
112 def keys(self):
113 calls.append('keys')
114 return ()
115 def items(self):
116 calls.append('items')
117 return ()
118
119 self.OrderedDict(Spam())
120 self.assertEqual(calls, ['keys'])
121
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200122 def test_fromkeys(self):
123 OrderedDict = self.OrderedDict
124 od = OrderedDict.fromkeys('abc')
125 self.assertEqual(list(od.items()), [(c, None) for c in 'abc'])
126 od = OrderedDict.fromkeys('abc', value=None)
127 self.assertEqual(list(od.items()), [(c, None) for c in 'abc'])
128 od = OrderedDict.fromkeys('abc', value=0)
129 self.assertEqual(list(od.items()), [(c, 0) for c in 'abc'])
130
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200131 def test_abc(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200132 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200133 self.assertIsInstance(OrderedDict(), MutableMapping)
134 self.assertTrue(issubclass(OrderedDict, MutableMapping))
135
136 def test_clear(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200137 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200138 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
139 shuffle(pairs)
140 od = OrderedDict(pairs)
141 self.assertEqual(len(od), len(pairs))
142 od.clear()
143 self.assertEqual(len(od), 0)
144
145 def test_delitem(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200146 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200147 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
148 od = OrderedDict(pairs)
149 del od['a']
150 self.assertNotIn('a', od)
151 with self.assertRaises(KeyError):
152 del od['a']
153 self.assertEqual(list(od.items()), pairs[:2] + pairs[3:])
154
155 def test_setitem(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200156 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200157 od = OrderedDict([('d', 1), ('b', 2), ('c', 3), ('a', 4), ('e', 5)])
158 od['c'] = 10 # existing element
159 od['f'] = 20 # new element
160 self.assertEqual(list(od.items()),
161 [('d', 1), ('b', 2), ('c', 10), ('a', 4), ('e', 5), ('f', 20)])
162
163 def test_iterators(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200164 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200165 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
166 shuffle(pairs)
167 od = OrderedDict(pairs)
168 self.assertEqual(list(od), [t[0] for t in pairs])
169 self.assertEqual(list(od.keys()), [t[0] for t in pairs])
170 self.assertEqual(list(od.values()), [t[1] for t in pairs])
171 self.assertEqual(list(od.items()), pairs)
172 self.assertEqual(list(reversed(od)),
173 [t[0] for t in reversed(pairs)])
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200174 self.assertEqual(list(reversed(od.keys())),
175 [t[0] for t in reversed(pairs)])
176 self.assertEqual(list(reversed(od.values())),
177 [t[1] for t in reversed(pairs)])
178 self.assertEqual(list(reversed(od.items())), list(reversed(pairs)))
179
180 def test_detect_deletion_during_iteration(self):
181 OrderedDict = self.OrderedDict
182 od = OrderedDict.fromkeys('abc')
183 it = iter(od)
184 key = next(it)
185 del od[key]
186 with self.assertRaises(Exception):
187 # Note, the exact exception raised is not guaranteed
188 # The only guarantee that the next() will not succeed
189 next(it)
190
191 def test_sorted_iterators(self):
192 OrderedDict = self.OrderedDict
193 with self.assertRaises(TypeError):
194 OrderedDict([('a', 1), ('b', 2)], None)
195 pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
196 od = OrderedDict(pairs)
197 self.assertEqual(sorted(od), [t[0] for t in pairs])
198 self.assertEqual(sorted(od.keys()), [t[0] for t in pairs])
199 self.assertEqual(sorted(od.values()), [t[1] for t in pairs])
200 self.assertEqual(sorted(od.items()), pairs)
201 self.assertEqual(sorted(reversed(od)),
202 sorted([t[0] for t in reversed(pairs)]))
203
204 def test_iterators_empty(self):
205 OrderedDict = self.OrderedDict
206 od = OrderedDict()
207 empty = []
208 self.assertEqual(list(od), empty)
209 self.assertEqual(list(od.keys()), empty)
210 self.assertEqual(list(od.values()), empty)
211 self.assertEqual(list(od.items()), empty)
212 self.assertEqual(list(reversed(od)), empty)
213 self.assertEqual(list(reversed(od.keys())), empty)
214 self.assertEqual(list(reversed(od.values())), empty)
215 self.assertEqual(list(reversed(od.items())), empty)
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200216
217 def test_popitem(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200218 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200219 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
220 shuffle(pairs)
221 od = OrderedDict(pairs)
222 while pairs:
223 self.assertEqual(od.popitem(), pairs.pop())
224 with self.assertRaises(KeyError):
225 od.popitem()
226 self.assertEqual(len(od), 0)
227
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200228 def test_popitem_last(self):
229 OrderedDict = self.OrderedDict
230 pairs = [(i, i) for i in range(30)]
231
232 obj = OrderedDict(pairs)
233 for i in range(8):
234 obj.popitem(True)
235 obj.popitem(True)
236 obj.popitem(last=True)
237 self.assertEqual(len(obj), 20)
238
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200239 def test_pop(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200240 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200241 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
242 shuffle(pairs)
243 od = OrderedDict(pairs)
244 shuffle(pairs)
245 while pairs:
246 k, v = pairs.pop()
247 self.assertEqual(od.pop(k), v)
248 with self.assertRaises(KeyError):
249 od.pop('xyz')
250 self.assertEqual(len(od), 0)
251 self.assertEqual(od.pop(k, 12345), 12345)
252
253 # make sure pop still works when __missing__ is defined
254 class Missing(OrderedDict):
255 def __missing__(self, key):
256 return 0
257 m = Missing(a=1)
258 self.assertEqual(m.pop('b', 5), 5)
259 self.assertEqual(m.pop('a', 6), 1)
260 self.assertEqual(m.pop('a', 6), 6)
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200261 self.assertEqual(m.pop('a', default=6), 6)
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200262 with self.assertRaises(KeyError):
263 m.pop('a')
264
265 def test_equality(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200266 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200267 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
268 shuffle(pairs)
269 od1 = OrderedDict(pairs)
270 od2 = OrderedDict(pairs)
271 self.assertEqual(od1, od2) # same order implies equality
272 pairs = pairs[2:] + pairs[:2]
273 od2 = OrderedDict(pairs)
274 self.assertNotEqual(od1, od2) # different order implies inequality
275 # comparison to regular dict is not order sensitive
276 self.assertEqual(od1, dict(od2))
277 self.assertEqual(dict(od2), od1)
278 # different length implied inequality
279 self.assertNotEqual(od1, OrderedDict(pairs[:-1]))
280
281 def test_copying(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200282 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200283 # Check that ordered dicts are copyable, deepcopyable, picklable,
284 # and have a repr/eval round-trip
285 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
286 od = OrderedDict(pairs)
287 def check(dup):
288 msg = "\ncopy: %s\nod: %s" % (dup, od)
289 self.assertIsNot(dup, od, msg)
290 self.assertEqual(dup, od)
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200291 self.assertEqual(list(dup.items()), list(od.items()))
292 self.assertEqual(len(dup), len(od))
293 self.assertEqual(type(dup), type(od))
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200294 check(od.copy())
295 check(copy.copy(od))
296 check(copy.deepcopy(od))
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200297 # pickle directly pulls the module, so we have to fake it
298 with replaced_module('collections', self.module):
299 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
300 with self.subTest(proto=proto):
301 check(pickle.loads(pickle.dumps(od, proto)))
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200302 check(eval(repr(od)))
303 update_test = OrderedDict()
304 update_test.update(od)
305 check(update_test)
306 check(OrderedDict(od))
307
308 def test_yaml_linkage(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200309 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200310 # Verify that __reduce__ is setup in a way that supports PyYAML's dump() feature.
311 # In yaml, lists are native but tuples are not.
312 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
313 od = OrderedDict(pairs)
314 # yaml.dump(od) -->
315 # '!!python/object/apply:__main__.OrderedDict\n- - [a, 1]\n - [b, 2]\n'
316 self.assertTrue(all(type(pair)==list for pair in od.__reduce__()[1]))
317
318 def test_reduce_not_too_fat(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200319 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200320 # do not save instance dictionary if not needed
321 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
322 od = OrderedDict(pairs)
Serhiy Storchakad2962f12016-02-08 16:39:05 +0200323 self.assertIsInstance(od.__dict__, dict)
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200324 self.assertIsNone(od.__reduce__()[2])
325 od.x = 10
Serhiy Storchakad2962f12016-02-08 16:39:05 +0200326 self.assertEqual(od.__dict__['x'], 10)
327 self.assertEqual(od.__reduce__()[2], {'x': 10})
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200328
329 def test_pickle_recursive(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200330 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200331 od = OrderedDict()
332 od[1] = od
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200333
334 # pickle directly pulls the module, so we have to fake it
335 with replaced_module('collections', self.module):
336 for proto in range(-1, pickle.HIGHEST_PROTOCOL + 1):
337 dup = pickle.loads(pickle.dumps(od, proto))
338 self.assertIsNot(dup, od)
339 self.assertEqual(list(dup.keys()), [1])
340 self.assertIs(dup[1], dup)
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200341
342 def test_repr(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200343 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200344 od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])
345 self.assertEqual(repr(od),
346 "OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
347 self.assertEqual(eval(repr(od)), od)
348 self.assertEqual(repr(OrderedDict()), "OrderedDict()")
349
350 def test_repr_recursive(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200351 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200352 # See issue #9826
353 od = OrderedDict.fromkeys('abc')
354 od['x'] = od
355 self.assertEqual(repr(od),
356 "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])")
357
bennorthd7773d92018-01-26 15:46:01 +0000358 def test_repr_recursive_values(self):
359 OrderedDict = self.OrderedDict
360 od = OrderedDict()
361 od[42] = od.values()
362 r = repr(od)
363 # Cannot perform a stronger test, as the contents of the repr
364 # are implementation-dependent. All we can say is that we
365 # want a str result, not an exception of any sort.
366 self.assertIsInstance(r, str)
367 od[42] = od.items()
368 r = repr(od)
369 # Again.
370 self.assertIsInstance(r, str)
371
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200372 def test_setdefault(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200373 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200374 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
375 shuffle(pairs)
376 od = OrderedDict(pairs)
377 pair_order = list(od.items())
378 self.assertEqual(od.setdefault('a', 10), 3)
379 # make sure order didn't change
380 self.assertEqual(list(od.items()), pair_order)
381 self.assertEqual(od.setdefault('x', 10), 10)
382 # make sure 'x' is added to the end
383 self.assertEqual(list(od.items())[-1], ('x', 10))
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200384 self.assertEqual(od.setdefault('g', default=9), 9)
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200385
386 # make sure setdefault still works when __missing__ is defined
387 class Missing(OrderedDict):
388 def __missing__(self, key):
389 return 0
390 self.assertEqual(Missing().setdefault(5, 9), 9)
391
392 def test_reinsert(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200393 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200394 # Given insert a, insert b, delete a, re-insert a,
395 # verify that a is now later than b.
396 od = OrderedDict()
397 od['a'] = 1
398 od['b'] = 2
399 del od['a']
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200400 self.assertEqual(list(od.items()), [('b', 2)])
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200401 od['a'] = 1
402 self.assertEqual(list(od.items()), [('b', 2), ('a', 1)])
403
404 def test_move_to_end(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200405 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200406 od = OrderedDict.fromkeys('abcde')
407 self.assertEqual(list(od), list('abcde'))
408 od.move_to_end('c')
409 self.assertEqual(list(od), list('abdec'))
410 od.move_to_end('c', 0)
411 self.assertEqual(list(od), list('cabde'))
412 od.move_to_end('c', 0)
413 self.assertEqual(list(od), list('cabde'))
414 od.move_to_end('e')
415 self.assertEqual(list(od), list('cabde'))
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200416 od.move_to_end('b', last=False)
417 self.assertEqual(list(od), list('bcade'))
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200418 with self.assertRaises(KeyError):
419 od.move_to_end('x')
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200420 with self.assertRaises(KeyError):
421 od.move_to_end('x', 0)
422
423 def test_move_to_end_issue25406(self):
424 OrderedDict = self.OrderedDict
425 od = OrderedDict.fromkeys('abc')
426 od.move_to_end('c', last=False)
427 self.assertEqual(list(od), list('cab'))
428 od.move_to_end('a', last=False)
429 self.assertEqual(list(od), list('acb'))
430
431 od = OrderedDict.fromkeys('abc')
432 od.move_to_end('a')
433 self.assertEqual(list(od), list('bca'))
434 od.move_to_end('c')
435 self.assertEqual(list(od), list('bac'))
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200436
437 def test_sizeof(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200438 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200439 # Wimpy test: Just verify the reported size is larger than a regular dict
440 d = dict(a=1)
441 od = OrderedDict(**d)
442 self.assertGreater(sys.getsizeof(od), sys.getsizeof(d))
443
Serhiy Storchakab64c9432015-11-25 17:18:57 +0200444 def test_views(self):
445 OrderedDict = self.OrderedDict
446 # See http://bugs.python.org/issue24286
447 s = 'the quick brown fox jumped over a lazy dog yesterday before dawn'.split()
448 od = OrderedDict.fromkeys(s)
449 self.assertEqual(od.keys(), dict(od).keys())
450 self.assertEqual(od.items(), dict(od).items())
451
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200452 def test_override_update(self):
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200453 OrderedDict = self.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200454 # Verify that subclasses can override update() without breaking __init__()
455 class MyOD(OrderedDict):
456 def update(self, *args, **kwds):
457 raise Exception()
458 items = [('a', 1), ('c', 3), ('b', 2)]
459 self.assertEqual(list(MyOD(items).items()), items)
460
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200461 def test_highly_nested(self):
Jeroen Demeyer351c6742019-05-10 19:21:11 +0200462 # Issues 25395 and 35983: test that the trashcan mechanism works
463 # correctly for OrderedDict: deleting a highly nested OrderDict
464 # should not crash Python.
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200465 OrderedDict = self.OrderedDict
466 obj = None
467 for _ in range(1000):
468 obj = OrderedDict([(None, obj)])
469 del obj
470 support.gc_collect()
471
472 def test_highly_nested_subclass(self):
Jeroen Demeyer351c6742019-05-10 19:21:11 +0200473 # Issues 25395 and 35983: test that the trashcan mechanism works
474 # correctly for OrderedDict: deleting a highly nested OrderDict
475 # should not crash Python.
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200476 OrderedDict = self.OrderedDict
477 deleted = []
478 class MyOD(OrderedDict):
479 def __del__(self):
480 deleted.append(self.i)
481 obj = None
482 for i in range(100):
483 obj = MyOD([(None, obj)])
484 obj.i = i
485 del obj
486 support.gc_collect()
487 self.assertEqual(deleted, list(reversed(range(100))))
488
489 def test_delitem_hash_collision(self):
490 OrderedDict = self.OrderedDict
491
492 class Key:
493 def __init__(self, hash):
494 self._hash = hash
495 self.value = str(id(self))
496 def __hash__(self):
497 return self._hash
498 def __eq__(self, other):
499 try:
500 return self.value == other.value
501 except AttributeError:
502 return False
503 def __repr__(self):
504 return self.value
505
506 def blocking_hash(hash):
507 # See the collision-handling in lookdict (in Objects/dictobject.c).
508 MINSIZE = 8
509 i = (hash & MINSIZE-1)
510 return (i << 2) + i + hash + 1
511
512 COLLIDING = 1
513
514 key = Key(COLLIDING)
515 colliding = Key(COLLIDING)
516 blocking = Key(blocking_hash(COLLIDING))
517
518 od = OrderedDict()
519 od[key] = ...
520 od[blocking] = ...
521 od[colliding] = ...
522 od['after'] = ...
523
524 del od[blocking]
525 del od[colliding]
526 self.assertEqual(list(od.items()), [(key, ...), ('after', ...)])
527
528 def test_issue24347(self):
529 OrderedDict = self.OrderedDict
530
531 class Key:
532 def __hash__(self):
533 return randrange(100000)
534
535 od = OrderedDict()
536 for i in range(100):
537 key = Key()
538 od[key] = i
539
540 # These should not crash.
541 with self.assertRaises(KeyError):
542 list(od.values())
543 with self.assertRaises(KeyError):
544 list(od.items())
545 with self.assertRaises(KeyError):
546 repr(od)
547 with self.assertRaises(KeyError):
548 od.copy()
549
550 def test_issue24348(self):
551 OrderedDict = self.OrderedDict
552
553 class Key:
554 def __hash__(self):
555 return 1
556
557 od = OrderedDict()
558 od[Key()] = 0
559 # This should not crash.
560 od.popitem()
561
562 def test_issue24667(self):
563 """
564 dict resizes after a certain number of insertion operations,
565 whether or not there were deletions that freed up slots in the
566 hash table. During fast node lookup, OrderedDict must correctly
567 respond to all resizes, even if the current "size" is the same
568 as the old one. We verify that here by forcing a dict resize
569 on a sparse odict and then perform an operation that should
570 trigger an odict resize (e.g. popitem). One key aspect here is
571 that we will keep the size of the odict the same at each popitem
572 call. This verifies that we handled the dict resize properly.
573 """
574 OrderedDict = self.OrderedDict
575
576 od = OrderedDict()
577 for c0 in '0123456789ABCDEF':
578 for c1 in '0123456789ABCDEF':
579 if len(od) == 4:
580 # This should not raise a KeyError.
581 od.popitem(last=False)
582 key = c0 + c1
583 od[key] = key
584
585 # Direct use of dict methods
586
587 def test_dict_setitem(self):
588 OrderedDict = self.OrderedDict
589 od = OrderedDict()
590 dict.__setitem__(od, 'spam', 1)
591 self.assertNotIn('NULL', repr(od))
592
593 def test_dict_delitem(self):
594 OrderedDict = self.OrderedDict
595 od = OrderedDict()
596 od['spam'] = 1
597 od['ham'] = 2
598 dict.__delitem__(od, 'spam')
599 with self.assertRaises(KeyError):
600 repr(od)
601
602 def test_dict_clear(self):
603 OrderedDict = self.OrderedDict
604 od = OrderedDict()
605 od['spam'] = 1
606 od['ham'] = 2
607 dict.clear(od)
608 self.assertNotIn('NULL', repr(od))
609
610 def test_dict_pop(self):
611 OrderedDict = self.OrderedDict
612 od = OrderedDict()
613 od['spam'] = 1
614 od['ham'] = 2
615 dict.pop(od, 'spam')
616 with self.assertRaises(KeyError):
617 repr(od)
618
619 def test_dict_popitem(self):
620 OrderedDict = self.OrderedDict
621 od = OrderedDict()
622 od['spam'] = 1
623 od['ham'] = 2
624 dict.popitem(od)
625 with self.assertRaises(KeyError):
626 repr(od)
627
628 def test_dict_setdefault(self):
629 OrderedDict = self.OrderedDict
630 od = OrderedDict()
631 dict.setdefault(od, 'spam', 1)
632 self.assertNotIn('NULL', repr(od))
633
634 def test_dict_update(self):
635 OrderedDict = self.OrderedDict
636 od = OrderedDict()
637 dict.update(od, [('spam', 1)])
638 self.assertNotIn('NULL', repr(od))
639
Serhiy Storchakad205d012016-01-19 14:46:25 +0200640 def test_reference_loop(self):
641 # Issue 25935
642 OrderedDict = self.OrderedDict
643 class A:
644 od = OrderedDict()
645 A.od[A] = None
646 r = weakref.ref(A)
647 del A
648 gc.collect()
649 self.assertIsNone(r())
650
Serhiy Storchakafbb1c5e2016-03-30 20:40:02 +0300651 def test_free_after_iterating(self):
652 support.check_free_after_iterating(self, iter, self.OrderedDict)
653 support.check_free_after_iterating(self, lambda d: iter(d.keys()), self.OrderedDict)
654 support.check_free_after_iterating(self, lambda d: iter(d.values()), self.OrderedDict)
655 support.check_free_after_iterating(self, lambda d: iter(d.items()), self.OrderedDict)
656
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200657
658class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
659
660 module = py_coll
661 OrderedDict = py_coll.OrderedDict
662
663
Victor Stinner742da042016-09-07 17:40:12 -0700664class CPythonBuiltinDictTests(unittest.TestCase):
665 """Builtin dict preserves insertion order.
666
667 Reuse some of tests in OrderedDict selectively.
668 """
669
670 module = builtins
671 OrderedDict = dict
672
673for method in (
674 "test_init test_update test_abc test_clear test_delitem " +
675 "test_setitem test_detect_deletion_during_iteration " +
676 "test_popitem test_reinsert test_override_update " +
677 "test_highly_nested test_highly_nested_subclass " +
678 "test_delitem_hash_collision ").split():
679 setattr(CPythonBuiltinDictTests, method, getattr(OrderedDictTests, method))
680del method
681
682
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200683@unittest.skipUnless(c_coll, 'requires the C version of the collections module')
684class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
685
686 module = c_coll
687 OrderedDict = c_coll.OrderedDict
Serhiy Storchaka0ce7a3a2015-12-22 08:16:18 +0200688 check_sizeof = support.check_sizeof
689
690 @support.cpython_only
691 def test_sizeof_exact(self):
692 OrderedDict = self.OrderedDict
693 calcsize = struct.calcsize
694 size = support.calcobjsize
695 check = self.check_sizeof
696
Victor Stinnera1fd0782016-09-09 21:51:19 -0700697 basicsize = size('nQ2P' + '3PnPn2P') + calcsize('2nP2n')
698
Victor Stinner742da042016-09-07 17:40:12 -0700699 entrysize = calcsize('n2P')
700 p = calcsize('P')
Serhiy Storchaka0ce7a3a2015-12-22 08:16:18 +0200701 nodesize = calcsize('Pn2P')
702
703 od = OrderedDict()
Serhiy Storchaka6f17e512018-10-20 02:27:45 +0300704 check(od, basicsize + 8 + 5*entrysize) # 8byte indices + 8*2//3 * entry table
Serhiy Storchaka0ce7a3a2015-12-22 08:16:18 +0200705 od.x = 1
Serhiy Storchaka6f17e512018-10-20 02:27:45 +0300706 check(od, basicsize + 8 + 5*entrysize)
Serhiy Storchaka0ce7a3a2015-12-22 08:16:18 +0200707 od.update([(i, i) for i in range(3)])
Victor Stinner742da042016-09-07 17:40:12 -0700708 check(od, basicsize + 8*p + 8 + 5*entrysize + 3*nodesize)
Serhiy Storchaka0ce7a3a2015-12-22 08:16:18 +0200709 od.update([(i, i) for i in range(3, 10)])
Victor Stinner742da042016-09-07 17:40:12 -0700710 check(od, basicsize + 16*p + 16 + 10*entrysize + 10*nodesize)
Serhiy Storchaka0ce7a3a2015-12-22 08:16:18 +0200711
712 check(od.keys(), size('P'))
713 check(od.items(), size('P'))
714 check(od.values(), size('P'))
715
716 itersize = size('iP2n2P')
717 check(iter(od), itersize)
718 check(iter(od.keys()), itersize)
719 check(iter(od.items()), itersize)
720 check(iter(od.values()), itersize)
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200721
722 def test_key_change_during_iteration(self):
723 OrderedDict = self.OrderedDict
724
725 od = OrderedDict.fromkeys('abcde')
726 self.assertEqual(list(od), list('abcde'))
727 with self.assertRaises(RuntimeError):
728 for i, k in enumerate(od):
729 od.move_to_end(k)
730 self.assertLess(i, 5)
731 with self.assertRaises(RuntimeError):
732 for k in od:
733 od['f'] = None
734 with self.assertRaises(RuntimeError):
735 for k in od:
736 del od['c']
737 self.assertEqual(list(od), list('bdeaf'))
738
Sergey Fedoseeva5259fb2018-10-20 10:20:39 +0500739 def test_iterators_pickling(self):
740 OrderedDict = self.OrderedDict
741 pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
742 od = OrderedDict(pairs)
743
744 for method_name in ('keys', 'values', 'items'):
745 meth = getattr(od, method_name)
746 expected = list(meth())[1:]
747 for i in range(pickle.HIGHEST_PROTOCOL + 1):
748 with self.subTest(method_name=method_name, protocol=i):
749 it = iter(meth())
750 next(it)
751 p = pickle.dumps(it, i)
752 unpickled = pickle.loads(p)
753 self.assertEqual(list(unpickled), expected)
754 self.assertEqual(list(it), expected)
755
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200756
757class PurePythonOrderedDictSubclassTests(PurePythonOrderedDictTests):
758
759 module = py_coll
760 class OrderedDict(py_coll.OrderedDict):
761 pass
762
763
764class CPythonOrderedDictSubclassTests(CPythonOrderedDictTests):
765
766 module = c_coll
767 class OrderedDict(c_coll.OrderedDict):
768 pass
769
770
771class PurePythonGeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
772
773 @classmethod
774 def setUpClass(cls):
775 cls.type2test = py_coll.OrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200776
777 def test_popitem(self):
778 d = self._empty_mapping()
779 self.assertRaises(KeyError, d.popitem)
780
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200781
Serhiy Storchaka2cefc1e2015-11-25 17:12:02 +0200782@unittest.skipUnless(c_coll, 'requires the C version of the collections module')
783class CPythonGeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
784
785 @classmethod
786 def setUpClass(cls):
787 cls.type2test = c_coll.OrderedDict
788
789 def test_popitem(self):
790 d = self._empty_mapping()
791 self.assertRaises(KeyError, d.popitem)
792
793
794class PurePythonSubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
795
796 @classmethod
797 def setUpClass(cls):
798 class MyOrderedDict(py_coll.OrderedDict):
799 pass
800 cls.type2test = MyOrderedDict
801
802 def test_popitem(self):
803 d = self._empty_mapping()
804 self.assertRaises(KeyError, d.popitem)
805
806
807@unittest.skipUnless(c_coll, 'requires the C version of the collections module')
808class CPythonSubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
809
810 @classmethod
811 def setUpClass(cls):
812 class MyOrderedDict(c_coll.OrderedDict):
813 pass
814 cls.type2test = MyOrderedDict
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200815
816 def test_popitem(self):
817 d = self._empty_mapping()
818 self.assertRaises(KeyError, d.popitem)
819
820
Serhiy Storchaka33e7ea52015-11-25 17:09:01 +0200821if __name__ == "__main__":
822 unittest.main()