|
1 import unittest |
|
2 from test import test_support |
|
3 |
|
4 import UserDict, random, string |
|
5 |
|
6 |
|
7 class DictTest(unittest.TestCase): |
|
8 def test_constructor(self): |
|
9 # calling built-in types without argument must return empty |
|
10 self.assertEqual(dict(), {}) |
|
11 self.assert_(dict() is not {}) |
|
12 |
|
13 def test_literal_constructor(self): |
|
14 # check literal constructor for different sized dicts (to exercise the BUILD_MAP oparg |
|
15 for n in (0, 1, 6, 256, 400): |
|
16 items = [(''.join([random.choice(string.letters) |
|
17 for j in range(8)]), |
|
18 i) |
|
19 for i in range(n)] |
|
20 random.shuffle(items) |
|
21 dictliteral = '{' + ', '.join('%r: %d' % item for item in items) + '}' |
|
22 self.assertEqual(eval(dictliteral), dict(items)) |
|
23 |
|
24 def test_bool(self): |
|
25 self.assert_(not {}) |
|
26 self.assert_({1: 2}) |
|
27 self.assert_(bool({}) is False) |
|
28 self.assert_(bool({1: 2}) is True) |
|
29 |
|
30 def test_keys(self): |
|
31 d = {} |
|
32 self.assertEqual(d.keys(), []) |
|
33 d = {'a': 1, 'b': 2} |
|
34 k = d.keys() |
|
35 self.assert_(d.has_key('a')) |
|
36 self.assert_(d.has_key('b')) |
|
37 |
|
38 self.assertRaises(TypeError, d.keys, None) |
|
39 |
|
40 def test_values(self): |
|
41 d = {} |
|
42 self.assertEqual(d.values(), []) |
|
43 d = {1:2} |
|
44 self.assertEqual(d.values(), [2]) |
|
45 |
|
46 self.assertRaises(TypeError, d.values, None) |
|
47 |
|
48 def test_items(self): |
|
49 d = {} |
|
50 self.assertEqual(d.items(), []) |
|
51 |
|
52 d = {1:2} |
|
53 self.assertEqual(d.items(), [(1, 2)]) |
|
54 |
|
55 self.assertRaises(TypeError, d.items, None) |
|
56 |
|
57 def test_has_key(self): |
|
58 d = {} |
|
59 self.assert_(not d.has_key('a')) |
|
60 d = {'a': 1, 'b': 2} |
|
61 k = d.keys() |
|
62 k.sort() |
|
63 self.assertEqual(k, ['a', 'b']) |
|
64 |
|
65 self.assertRaises(TypeError, d.has_key) |
|
66 |
|
67 def test_contains(self): |
|
68 d = {} |
|
69 self.assert_(not ('a' in d)) |
|
70 self.assert_('a' not in d) |
|
71 d = {'a': 1, 'b': 2} |
|
72 self.assert_('a' in d) |
|
73 self.assert_('b' in d) |
|
74 self.assert_('c' not in d) |
|
75 |
|
76 self.assertRaises(TypeError, d.__contains__) |
|
77 |
|
78 def test_len(self): |
|
79 d = {} |
|
80 self.assertEqual(len(d), 0) |
|
81 d = {'a': 1, 'b': 2} |
|
82 self.assertEqual(len(d), 2) |
|
83 |
|
84 def test_getitem(self): |
|
85 d = {'a': 1, 'b': 2} |
|
86 self.assertEqual(d['a'], 1) |
|
87 self.assertEqual(d['b'], 2) |
|
88 d['c'] = 3 |
|
89 d['a'] = 4 |
|
90 self.assertEqual(d['c'], 3) |
|
91 self.assertEqual(d['a'], 4) |
|
92 del d['b'] |
|
93 self.assertEqual(d, {'a': 4, 'c': 3}) |
|
94 |
|
95 self.assertRaises(TypeError, d.__getitem__) |
|
96 |
|
97 class BadEq(object): |
|
98 def __eq__(self, other): |
|
99 raise Exc() |
|
100 def __hash__(self): |
|
101 return 24 |
|
102 |
|
103 d = {} |
|
104 d[BadEq()] = 42 |
|
105 self.assertRaises(KeyError, d.__getitem__, 23) |
|
106 |
|
107 class Exc(Exception): pass |
|
108 |
|
109 class BadHash(object): |
|
110 fail = False |
|
111 def __hash__(self): |
|
112 if self.fail: |
|
113 raise Exc() |
|
114 else: |
|
115 return 42 |
|
116 |
|
117 x = BadHash() |
|
118 d[x] = 42 |
|
119 x.fail = True |
|
120 self.assertRaises(Exc, d.__getitem__, x) |
|
121 |
|
122 def test_clear(self): |
|
123 d = {1:1, 2:2, 3:3} |
|
124 d.clear() |
|
125 self.assertEqual(d, {}) |
|
126 |
|
127 self.assertRaises(TypeError, d.clear, None) |
|
128 |
|
129 def test_update(self): |
|
130 d = {} |
|
131 d.update({1:100}) |
|
132 d.update({2:20}) |
|
133 d.update({1:1, 2:2, 3:3}) |
|
134 self.assertEqual(d, {1:1, 2:2, 3:3}) |
|
135 |
|
136 d.update() |
|
137 self.assertEqual(d, {1:1, 2:2, 3:3}) |
|
138 |
|
139 self.assertRaises((TypeError, AttributeError), d.update, None) |
|
140 |
|
141 class SimpleUserDict: |
|
142 def __init__(self): |
|
143 self.d = {1:1, 2:2, 3:3} |
|
144 def keys(self): |
|
145 return self.d.keys() |
|
146 def __getitem__(self, i): |
|
147 return self.d[i] |
|
148 d.clear() |
|
149 d.update(SimpleUserDict()) |
|
150 self.assertEqual(d, {1:1, 2:2, 3:3}) |
|
151 |
|
152 class Exc(Exception): pass |
|
153 |
|
154 d.clear() |
|
155 class FailingUserDict: |
|
156 def keys(self): |
|
157 raise Exc |
|
158 self.assertRaises(Exc, d.update, FailingUserDict()) |
|
159 |
|
160 class FailingUserDict: |
|
161 def keys(self): |
|
162 class BogonIter: |
|
163 def __init__(self): |
|
164 self.i = 1 |
|
165 def __iter__(self): |
|
166 return self |
|
167 def next(self): |
|
168 if self.i: |
|
169 self.i = 0 |
|
170 return 'a' |
|
171 raise Exc |
|
172 return BogonIter() |
|
173 def __getitem__(self, key): |
|
174 return key |
|
175 self.assertRaises(Exc, d.update, FailingUserDict()) |
|
176 |
|
177 class FailingUserDict: |
|
178 def keys(self): |
|
179 class BogonIter: |
|
180 def __init__(self): |
|
181 self.i = ord('a') |
|
182 def __iter__(self): |
|
183 return self |
|
184 def next(self): |
|
185 if self.i <= ord('z'): |
|
186 rtn = chr(self.i) |
|
187 self.i += 1 |
|
188 return rtn |
|
189 raise StopIteration |
|
190 return BogonIter() |
|
191 def __getitem__(self, key): |
|
192 raise Exc |
|
193 self.assertRaises(Exc, d.update, FailingUserDict()) |
|
194 |
|
195 class badseq(object): |
|
196 def __iter__(self): |
|
197 return self |
|
198 def next(self): |
|
199 raise Exc() |
|
200 |
|
201 self.assertRaises(Exc, {}.update, badseq()) |
|
202 |
|
203 self.assertRaises(ValueError, {}.update, [(1, 2, 3)]) |
|
204 |
|
205 def test_fromkeys(self): |
|
206 self.assertEqual(dict.fromkeys('abc'), {'a':None, 'b':None, 'c':None}) |
|
207 d = {} |
|
208 self.assert_(not(d.fromkeys('abc') is d)) |
|
209 self.assertEqual(d.fromkeys('abc'), {'a':None, 'b':None, 'c':None}) |
|
210 self.assertEqual(d.fromkeys((4,5),0), {4:0, 5:0}) |
|
211 self.assertEqual(d.fromkeys([]), {}) |
|
212 def g(): |
|
213 yield 1 |
|
214 self.assertEqual(d.fromkeys(g()), {1:None}) |
|
215 self.assertRaises(TypeError, {}.fromkeys, 3) |
|
216 class dictlike(dict): pass |
|
217 self.assertEqual(dictlike.fromkeys('a'), {'a':None}) |
|
218 self.assertEqual(dictlike().fromkeys('a'), {'a':None}) |
|
219 self.assert_(type(dictlike.fromkeys('a')) is dictlike) |
|
220 self.assert_(type(dictlike().fromkeys('a')) is dictlike) |
|
221 class mydict(dict): |
|
222 def __new__(cls): |
|
223 return UserDict.UserDict() |
|
224 ud = mydict.fromkeys('ab') |
|
225 self.assertEqual(ud, {'a':None, 'b':None}) |
|
226 self.assert_(isinstance(ud, UserDict.UserDict)) |
|
227 self.assertRaises(TypeError, dict.fromkeys) |
|
228 |
|
229 class Exc(Exception): pass |
|
230 |
|
231 class baddict1(dict): |
|
232 def __init__(self): |
|
233 raise Exc() |
|
234 |
|
235 self.assertRaises(Exc, baddict1.fromkeys, [1]) |
|
236 |
|
237 class BadSeq(object): |
|
238 def __iter__(self): |
|
239 return self |
|
240 def next(self): |
|
241 raise Exc() |
|
242 |
|
243 self.assertRaises(Exc, dict.fromkeys, BadSeq()) |
|
244 |
|
245 class baddict2(dict): |
|
246 def __setitem__(self, key, value): |
|
247 raise Exc() |
|
248 |
|
249 self.assertRaises(Exc, baddict2.fromkeys, [1]) |
|
250 |
|
251 # test fast path for dictionary inputs |
|
252 d = dict(zip(range(6), range(6))) |
|
253 self.assertEqual(dict.fromkeys(d, 0), dict(zip(range(6), [0]*6))) |
|
254 |
|
255 def test_copy(self): |
|
256 d = {1:1, 2:2, 3:3} |
|
257 self.assertEqual(d.copy(), {1:1, 2:2, 3:3}) |
|
258 self.assertEqual({}.copy(), {}) |
|
259 self.assertRaises(TypeError, d.copy, None) |
|
260 |
|
261 def test_get(self): |
|
262 d = {} |
|
263 self.assert_(d.get('c') is None) |
|
264 self.assertEqual(d.get('c', 3), 3) |
|
265 d = {'a' : 1, 'b' : 2} |
|
266 self.assert_(d.get('c') is None) |
|
267 self.assertEqual(d.get('c', 3), 3) |
|
268 self.assertEqual(d.get('a'), 1) |
|
269 self.assertEqual(d.get('a', 3), 1) |
|
270 self.assertRaises(TypeError, d.get) |
|
271 self.assertRaises(TypeError, d.get, None, None, None) |
|
272 |
|
273 def test_setdefault(self): |
|
274 # dict.setdefault() |
|
275 d = {} |
|
276 self.assert_(d.setdefault('key0') is None) |
|
277 d.setdefault('key0', []) |
|
278 self.assert_(d.setdefault('key0') is None) |
|
279 d.setdefault('key', []).append(3) |
|
280 self.assertEqual(d['key'][0], 3) |
|
281 d.setdefault('key', []).append(4) |
|
282 self.assertEqual(len(d['key']), 2) |
|
283 self.assertRaises(TypeError, d.setdefault) |
|
284 |
|
285 class Exc(Exception): pass |
|
286 |
|
287 class BadHash(object): |
|
288 fail = False |
|
289 def __hash__(self): |
|
290 if self.fail: |
|
291 raise Exc() |
|
292 else: |
|
293 return 42 |
|
294 |
|
295 x = BadHash() |
|
296 d[x] = 42 |
|
297 x.fail = True |
|
298 self.assertRaises(Exc, d.setdefault, x, []) |
|
299 |
|
300 def test_popitem(self): |
|
301 # dict.popitem() |
|
302 for copymode in -1, +1: |
|
303 # -1: b has same structure as a |
|
304 # +1: b is a.copy() |
|
305 for log2size in range(12): |
|
306 size = 2**log2size |
|
307 a = {} |
|
308 b = {} |
|
309 for i in range(size): |
|
310 a[repr(i)] = i |
|
311 if copymode < 0: |
|
312 b[repr(i)] = i |
|
313 if copymode > 0: |
|
314 b = a.copy() |
|
315 for i in range(size): |
|
316 ka, va = ta = a.popitem() |
|
317 self.assertEqual(va, int(ka)) |
|
318 kb, vb = tb = b.popitem() |
|
319 self.assertEqual(vb, int(kb)) |
|
320 self.assert_(not(copymode < 0 and ta != tb)) |
|
321 self.assert_(not a) |
|
322 self.assert_(not b) |
|
323 |
|
324 d = {} |
|
325 self.assertRaises(KeyError, d.popitem) |
|
326 |
|
327 def test_pop(self): |
|
328 # Tests for pop with specified key |
|
329 d = {} |
|
330 k, v = 'abc', 'def' |
|
331 d[k] = v |
|
332 self.assertRaises(KeyError, d.pop, 'ghi') |
|
333 |
|
334 self.assertEqual(d.pop(k), v) |
|
335 self.assertEqual(len(d), 0) |
|
336 |
|
337 self.assertRaises(KeyError, d.pop, k) |
|
338 |
|
339 # verify longs/ints get same value when key > 32 bits (for 64-bit archs) |
|
340 # see SF bug #689659 |
|
341 x = 4503599627370496L |
|
342 y = 4503599627370496 |
|
343 h = {x: 'anything', y: 'something else'} |
|
344 self.assertEqual(h[x], h[y]) |
|
345 |
|
346 self.assertEqual(d.pop(k, v), v) |
|
347 d[k] = v |
|
348 self.assertEqual(d.pop(k, 1), v) |
|
349 |
|
350 self.assertRaises(TypeError, d.pop) |
|
351 |
|
352 class Exc(Exception): pass |
|
353 |
|
354 class BadHash(object): |
|
355 fail = False |
|
356 def __hash__(self): |
|
357 if self.fail: |
|
358 raise Exc() |
|
359 else: |
|
360 return 42 |
|
361 |
|
362 x = BadHash() |
|
363 d[x] = 42 |
|
364 x.fail = True |
|
365 self.assertRaises(Exc, d.pop, x) |
|
366 |
|
367 def test_mutatingiteration(self): |
|
368 d = {} |
|
369 d[1] = 1 |
|
370 try: |
|
371 for i in d: |
|
372 d[i+1] = 1 |
|
373 except RuntimeError: |
|
374 pass |
|
375 else: |
|
376 self.fail("changing dict size during iteration doesn't raise Error") |
|
377 |
|
378 def test_repr(self): |
|
379 d = {} |
|
380 self.assertEqual(repr(d), '{}') |
|
381 d[1] = 2 |
|
382 self.assertEqual(repr(d), '{1: 2}') |
|
383 d = {} |
|
384 d[1] = d |
|
385 self.assertEqual(repr(d), '{1: {...}}') |
|
386 |
|
387 class Exc(Exception): pass |
|
388 |
|
389 class BadRepr(object): |
|
390 def __repr__(self): |
|
391 raise Exc() |
|
392 |
|
393 d = {1: BadRepr()} |
|
394 self.assertRaises(Exc, repr, d) |
|
395 |
|
396 def test_le(self): |
|
397 self.assert_(not ({} < {})) |
|
398 self.assert_(not ({1: 2} < {1L: 2L})) |
|
399 |
|
400 class Exc(Exception): pass |
|
401 |
|
402 class BadCmp(object): |
|
403 def __eq__(self, other): |
|
404 raise Exc() |
|
405 def __hash__(self): |
|
406 return 42 |
|
407 |
|
408 d1 = {BadCmp(): 1} |
|
409 d2 = {1: 1} |
|
410 try: |
|
411 d1 < d2 |
|
412 except Exc: |
|
413 pass |
|
414 else: |
|
415 self.fail("< didn't raise Exc") |
|
416 |
|
417 def test_missing(self): |
|
418 # Make sure dict doesn't have a __missing__ method |
|
419 self.assertEqual(hasattr(dict, "__missing__"), False) |
|
420 self.assertEqual(hasattr({}, "__missing__"), False) |
|
421 # Test several cases: |
|
422 # (D) subclass defines __missing__ method returning a value |
|
423 # (E) subclass defines __missing__ method raising RuntimeError |
|
424 # (F) subclass sets __missing__ instance variable (no effect) |
|
425 # (G) subclass doesn't define __missing__ at a all |
|
426 class D(dict): |
|
427 def __missing__(self, key): |
|
428 return 42 |
|
429 d = D({1: 2, 3: 4}) |
|
430 self.assertEqual(d[1], 2) |
|
431 self.assertEqual(d[3], 4) |
|
432 self.assert_(2 not in d) |
|
433 self.assert_(2 not in d.keys()) |
|
434 self.assertEqual(d[2], 42) |
|
435 class E(dict): |
|
436 def __missing__(self, key): |
|
437 raise RuntimeError(key) |
|
438 e = E() |
|
439 try: |
|
440 e[42] |
|
441 except RuntimeError, err: |
|
442 self.assertEqual(err.args, (42,)) |
|
443 else: |
|
444 self.fail("e[42] didn't raise RuntimeError") |
|
445 class F(dict): |
|
446 def __init__(self): |
|
447 # An instance variable __missing__ should have no effect |
|
448 self.__missing__ = lambda key: None |
|
449 f = F() |
|
450 try: |
|
451 f[42] |
|
452 except KeyError, err: |
|
453 self.assertEqual(err.args, (42,)) |
|
454 else: |
|
455 self.fail("f[42] didn't raise KeyError") |
|
456 class G(dict): |
|
457 pass |
|
458 g = G() |
|
459 try: |
|
460 g[42] |
|
461 except KeyError, err: |
|
462 self.assertEqual(err.args, (42,)) |
|
463 else: |
|
464 self.fail("g[42] didn't raise KeyError") |
|
465 |
|
466 def test_tuple_keyerror(self): |
|
467 # SF #1576657 |
|
468 d = {} |
|
469 try: |
|
470 d[(1,)] |
|
471 except KeyError, e: |
|
472 self.assertEqual(e.args, ((1,),)) |
|
473 else: |
|
474 self.fail("missing KeyError") |
|
475 |
|
476 def test_bad_key(self): |
|
477 # Dictionary lookups should fail if __cmp__() raises an exception. |
|
478 class CustomException(Exception): |
|
479 pass |
|
480 |
|
481 class BadDictKey: |
|
482 def __hash__(self): |
|
483 return hash(self.__class__) |
|
484 |
|
485 def __cmp__(self, other): |
|
486 if isinstance(other, self.__class__): |
|
487 raise CustomException |
|
488 return other |
|
489 |
|
490 d = {} |
|
491 x1 = BadDictKey() |
|
492 x2 = BadDictKey() |
|
493 d[x1] = 1 |
|
494 for stmt in ['d[x2] = 2', |
|
495 'z = d[x2]', |
|
496 'x2 in d', |
|
497 'd.has_key(x2)', |
|
498 'd.get(x2)', |
|
499 'd.setdefault(x2, 42)', |
|
500 'd.pop(x2)', |
|
501 'd.update({x2: 2})']: |
|
502 try: |
|
503 exec stmt in locals() |
|
504 except CustomException: |
|
505 pass |
|
506 else: |
|
507 self.fail("Statement didn't raise exception") |
|
508 |
|
509 def test_resize1(self): |
|
510 # Dict resizing bug, found by Jack Jansen in 2.2 CVS development. |
|
511 # This version got an assert failure in debug build, infinite loop in |
|
512 # release build. Unfortunately, provoking this kind of stuff requires |
|
513 # a mix of inserts and deletes hitting exactly the right hash codes in |
|
514 # exactly the right order, and I can't think of a randomized approach |
|
515 # that would be *likely* to hit a failing case in reasonable time. |
|
516 |
|
517 d = {} |
|
518 for i in range(5): |
|
519 d[i] = i |
|
520 for i in range(5): |
|
521 del d[i] |
|
522 for i in range(5, 9): # i==8 was the problem |
|
523 d[i] = i |
|
524 |
|
525 def test_resize2(self): |
|
526 # Another dict resizing bug (SF bug #1456209). |
|
527 # This caused Segmentation faults or Illegal instructions. |
|
528 |
|
529 class X(object): |
|
530 def __hash__(self): |
|
531 return 5 |
|
532 def __eq__(self, other): |
|
533 if resizing: |
|
534 d.clear() |
|
535 return False |
|
536 d = {} |
|
537 resizing = False |
|
538 d[X()] = 1 |
|
539 d[X()] = 2 |
|
540 d[X()] = 3 |
|
541 d[X()] = 4 |
|
542 d[X()] = 5 |
|
543 # now trigger a resize |
|
544 resizing = True |
|
545 d[9] = 6 |
|
546 |
|
547 def test_empty_presized_dict_in_freelist(self): |
|
548 # Bug #3537: if an empty but presized dict with a size larger |
|
549 # than 7 was in the freelist, it triggered an assertion failure |
|
550 try: |
|
551 d = {'a': 1/0, 'b': None, 'c': None, 'd': None, 'e': None, |
|
552 'f': None, 'g': None, 'h': None} |
|
553 except ZeroDivisionError: |
|
554 pass |
|
555 d = {} |
|
556 |
|
557 |
|
558 |
|
559 from test import mapping_tests |
|
560 |
|
561 class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): |
|
562 type2test = dict |
|
563 |
|
564 class Dict(dict): |
|
565 pass |
|
566 |
|
567 class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol): |
|
568 type2test = Dict |
|
569 |
|
570 def test_main(): |
|
571 test_support.run_unittest( |
|
572 DictTest, |
|
573 GeneralMappingTests, |
|
574 SubclassMappingTests, |
|
575 ) |
|
576 |
|
577 if __name__ == "__main__": |
|
578 test_main() |