|
1 "Test the functionality of Python classes implementing operators." |
|
2 |
|
3 import unittest |
|
4 |
|
5 from test import test_support |
|
6 |
|
7 testmeths = [ |
|
8 |
|
9 # Binary operations |
|
10 "add", |
|
11 "radd", |
|
12 "sub", |
|
13 "rsub", |
|
14 "mul", |
|
15 "rmul", |
|
16 "div", |
|
17 "rdiv", |
|
18 "mod", |
|
19 "rmod", |
|
20 "divmod", |
|
21 "rdivmod", |
|
22 "pow", |
|
23 "rpow", |
|
24 "rshift", |
|
25 "rrshift", |
|
26 "lshift", |
|
27 "rlshift", |
|
28 "and", |
|
29 "rand", |
|
30 "or", |
|
31 "ror", |
|
32 "xor", |
|
33 "rxor", |
|
34 |
|
35 # List/dict operations |
|
36 "contains", |
|
37 "getitem", |
|
38 "getslice", |
|
39 "setitem", |
|
40 "setslice", |
|
41 "delitem", |
|
42 "delslice", |
|
43 |
|
44 # Unary operations |
|
45 "neg", |
|
46 "pos", |
|
47 "abs", |
|
48 |
|
49 # generic operations |
|
50 "init", |
|
51 ] |
|
52 |
|
53 # These need to return something other than None |
|
54 # "coerce", |
|
55 # "hash", |
|
56 # "str", |
|
57 # "repr", |
|
58 # "int", |
|
59 # "long", |
|
60 # "float", |
|
61 # "oct", |
|
62 # "hex", |
|
63 |
|
64 # These are separate because they can influence the test of other methods. |
|
65 # "getattr", |
|
66 # "setattr", |
|
67 # "delattr", |
|
68 |
|
69 callLst = [] |
|
70 def trackCall(f): |
|
71 def track(*args, **kwargs): |
|
72 callLst.append((f.__name__, args)) |
|
73 return f(*args, **kwargs) |
|
74 return track |
|
75 |
|
76 class AllTests: |
|
77 trackCall = trackCall |
|
78 |
|
79 @trackCall |
|
80 def __coerce__(self, *args): |
|
81 return (self,) + args |
|
82 |
|
83 @trackCall |
|
84 def __hash__(self, *args): |
|
85 return hash(id(self)) |
|
86 |
|
87 @trackCall |
|
88 def __str__(self, *args): |
|
89 return "AllTests" |
|
90 |
|
91 @trackCall |
|
92 def __repr__(self, *args): |
|
93 return "AllTests" |
|
94 |
|
95 @trackCall |
|
96 def __int__(self, *args): |
|
97 return 1 |
|
98 |
|
99 @trackCall |
|
100 def __float__(self, *args): |
|
101 return 1.0 |
|
102 |
|
103 @trackCall |
|
104 def __long__(self, *args): |
|
105 return 1L |
|
106 |
|
107 @trackCall |
|
108 def __oct__(self, *args): |
|
109 return '01' |
|
110 |
|
111 @trackCall |
|
112 def __hex__(self, *args): |
|
113 return '0x1' |
|
114 |
|
115 @trackCall |
|
116 def __cmp__(self, *args): |
|
117 return 0 |
|
118 |
|
119 # Synthesize all the other AllTests methods from the names in testmeths. |
|
120 |
|
121 method_template = """\ |
|
122 @trackCall |
|
123 def __%(method)s__(self, *args): |
|
124 pass |
|
125 """ |
|
126 |
|
127 for method in testmeths: |
|
128 exec method_template % locals() in AllTests.__dict__ |
|
129 |
|
130 del method, method_template |
|
131 |
|
132 class ClassTests(unittest.TestCase): |
|
133 def setUp(self): |
|
134 callLst[:] = [] |
|
135 |
|
136 def assertCallStack(self, expected_calls): |
|
137 actualCallList = callLst[:] # need to copy because the comparison below will add |
|
138 # additional calls to callLst |
|
139 if expected_calls != actualCallList: |
|
140 self.fail("Expected call list:\n %s\ndoes not match actual call list\n %s" % |
|
141 (expected_calls, actualCallList)) |
|
142 |
|
143 def testInit(self): |
|
144 foo = AllTests() |
|
145 self.assertCallStack([("__init__", (foo,))]) |
|
146 |
|
147 def testBinaryOps(self): |
|
148 testme = AllTests() |
|
149 # Binary operations |
|
150 |
|
151 callLst[:] = [] |
|
152 testme + 1 |
|
153 self.assertCallStack([("__coerce__", (testme, 1)), ("__add__", (testme, 1))]) |
|
154 |
|
155 callLst[:] = [] |
|
156 1 + testme |
|
157 self.assertCallStack([("__coerce__", (testme, 1)), ("__radd__", (testme, 1))]) |
|
158 |
|
159 callLst[:] = [] |
|
160 testme - 1 |
|
161 self.assertCallStack([("__coerce__", (testme, 1)), ("__sub__", (testme, 1))]) |
|
162 |
|
163 callLst[:] = [] |
|
164 1 - testme |
|
165 self.assertCallStack([("__coerce__", (testme, 1)), ("__rsub__", (testme, 1))]) |
|
166 |
|
167 callLst[:] = [] |
|
168 testme * 1 |
|
169 self.assertCallStack([("__coerce__", (testme, 1)), ("__mul__", (testme, 1))]) |
|
170 |
|
171 callLst[:] = [] |
|
172 1 * testme |
|
173 self.assertCallStack([("__coerce__", (testme, 1)), ("__rmul__", (testme, 1))]) |
|
174 |
|
175 if 1/2 == 0: |
|
176 callLst[:] = [] |
|
177 testme / 1 |
|
178 self.assertCallStack([("__coerce__", (testme, 1)), ("__div__", (testme, 1))]) |
|
179 |
|
180 |
|
181 callLst[:] = [] |
|
182 1 / testme |
|
183 self.assertCallStack([("__coerce__", (testme, 1)), ("__rdiv__", (testme, 1))]) |
|
184 |
|
185 callLst[:] = [] |
|
186 testme % 1 |
|
187 self.assertCallStack([("__coerce__", (testme, 1)), ("__mod__", (testme, 1))]) |
|
188 |
|
189 callLst[:] = [] |
|
190 1 % testme |
|
191 self.assertCallStack([("__coerce__", (testme, 1)), ("__rmod__", (testme, 1))]) |
|
192 |
|
193 |
|
194 callLst[:] = [] |
|
195 divmod(testme,1) |
|
196 self.assertCallStack([("__coerce__", (testme, 1)), ("__divmod__", (testme, 1))]) |
|
197 |
|
198 callLst[:] = [] |
|
199 divmod(1, testme) |
|
200 self.assertCallStack([("__coerce__", (testme, 1)), ("__rdivmod__", (testme, 1))]) |
|
201 |
|
202 callLst[:] = [] |
|
203 testme ** 1 |
|
204 self.assertCallStack([("__coerce__", (testme, 1)), ("__pow__", (testme, 1))]) |
|
205 |
|
206 callLst[:] = [] |
|
207 1 ** testme |
|
208 self.assertCallStack([("__coerce__", (testme, 1)), ("__rpow__", (testme, 1))]) |
|
209 |
|
210 callLst[:] = [] |
|
211 testme >> 1 |
|
212 self.assertCallStack([("__coerce__", (testme, 1)), ("__rshift__", (testme, 1))]) |
|
213 |
|
214 callLst[:] = [] |
|
215 1 >> testme |
|
216 self.assertCallStack([("__coerce__", (testme, 1)), ("__rrshift__", (testme, 1))]) |
|
217 |
|
218 callLst[:] = [] |
|
219 testme << 1 |
|
220 self.assertCallStack([("__coerce__", (testme, 1)), ("__lshift__", (testme, 1))]) |
|
221 |
|
222 callLst[:] = [] |
|
223 1 << testme |
|
224 self.assertCallStack([("__coerce__", (testme, 1)), ("__rlshift__", (testme, 1))]) |
|
225 |
|
226 callLst[:] = [] |
|
227 testme & 1 |
|
228 self.assertCallStack([("__coerce__", (testme, 1)), ("__and__", (testme, 1))]) |
|
229 |
|
230 callLst[:] = [] |
|
231 1 & testme |
|
232 self.assertCallStack([("__coerce__", (testme, 1)), ("__rand__", (testme, 1))]) |
|
233 |
|
234 callLst[:] = [] |
|
235 testme | 1 |
|
236 self.assertCallStack([("__coerce__", (testme, 1)), ("__or__", (testme, 1))]) |
|
237 |
|
238 callLst[:] = [] |
|
239 1 | testme |
|
240 self.assertCallStack([("__coerce__", (testme, 1)), ("__ror__", (testme, 1))]) |
|
241 |
|
242 callLst[:] = [] |
|
243 testme ^ 1 |
|
244 self.assertCallStack([("__coerce__", (testme, 1)), ("__xor__", (testme, 1))]) |
|
245 |
|
246 callLst[:] = [] |
|
247 1 ^ testme |
|
248 self.assertCallStack([("__coerce__", (testme, 1)), ("__rxor__", (testme, 1))]) |
|
249 |
|
250 def testListAndDictOps(self): |
|
251 testme = AllTests() |
|
252 |
|
253 # List/dict operations |
|
254 |
|
255 class Empty: pass |
|
256 |
|
257 try: |
|
258 1 in Empty() |
|
259 self.fail('failed, should have raised TypeError') |
|
260 except TypeError: |
|
261 pass |
|
262 |
|
263 callLst[:] = [] |
|
264 1 in testme |
|
265 self.assertCallStack([('__contains__', (testme, 1))]) |
|
266 |
|
267 callLst[:] = [] |
|
268 testme[1] |
|
269 self.assertCallStack([('__getitem__', (testme, 1))]) |
|
270 |
|
271 callLst[:] = [] |
|
272 testme[1] = 1 |
|
273 self.assertCallStack([('__setitem__', (testme, 1, 1))]) |
|
274 |
|
275 callLst[:] = [] |
|
276 del testme[1] |
|
277 self.assertCallStack([('__delitem__', (testme, 1))]) |
|
278 |
|
279 callLst[:] = [] |
|
280 testme[:42] |
|
281 self.assertCallStack([('__getslice__', (testme, 0, 42))]) |
|
282 |
|
283 callLst[:] = [] |
|
284 testme[:42] = "The Answer" |
|
285 self.assertCallStack([('__setslice__', (testme, 0, 42, "The Answer"))]) |
|
286 |
|
287 callLst[:] = [] |
|
288 del testme[:42] |
|
289 self.assertCallStack([('__delslice__', (testme, 0, 42))]) |
|
290 |
|
291 callLst[:] = [] |
|
292 testme[2:1024:10] |
|
293 self.assertCallStack([('__getitem__', (testme, slice(2, 1024, 10)))]) |
|
294 |
|
295 callLst[:] = [] |
|
296 testme[2:1024:10] = "A lot" |
|
297 self.assertCallStack([('__setitem__', (testme, slice(2, 1024, 10), |
|
298 "A lot"))]) |
|
299 callLst[:] = [] |
|
300 del testme[2:1024:10] |
|
301 self.assertCallStack([('__delitem__', (testme, slice(2, 1024, 10)))]) |
|
302 |
|
303 callLst[:] = [] |
|
304 testme[:42, ..., :24:, 24, 100] |
|
305 self.assertCallStack([('__getitem__', (testme, (slice(None, 42, None), |
|
306 Ellipsis, |
|
307 slice(None, 24, None), |
|
308 24, 100)))]) |
|
309 callLst[:] = [] |
|
310 testme[:42, ..., :24:, 24, 100] = "Strange" |
|
311 self.assertCallStack([('__setitem__', (testme, (slice(None, 42, None), |
|
312 Ellipsis, |
|
313 slice(None, 24, None), |
|
314 24, 100), "Strange"))]) |
|
315 callLst[:] = [] |
|
316 del testme[:42, ..., :24:, 24, 100] |
|
317 self.assertCallStack([('__delitem__', (testme, (slice(None, 42, None), |
|
318 Ellipsis, |
|
319 slice(None, 24, None), |
|
320 24, 100)))]) |
|
321 |
|
322 # Now remove the slice hooks to see if converting normal slices to |
|
323 # slice object works. |
|
324 |
|
325 getslice = AllTests.__getslice__ |
|
326 del AllTests.__getslice__ |
|
327 setslice = AllTests.__setslice__ |
|
328 del AllTests.__setslice__ |
|
329 delslice = AllTests.__delslice__ |
|
330 del AllTests.__delslice__ |
|
331 |
|
332 # XXX when using new-style classes the slice testme[:42] produces |
|
333 # slice(None, 42, None) instead of slice(0, 42, None). py3k will have |
|
334 # to change this test. |
|
335 callLst[:] = [] |
|
336 testme[:42] |
|
337 self.assertCallStack([('__getitem__', (testme, slice(0, 42, None)))]) |
|
338 |
|
339 callLst[:] = [] |
|
340 testme[:42] = "The Answer" |
|
341 self.assertCallStack([('__setitem__', (testme, slice(0, 42, None), |
|
342 "The Answer"))]) |
|
343 callLst[:] = [] |
|
344 del testme[:42] |
|
345 self.assertCallStack([('__delitem__', (testme, slice(0, 42, None)))]) |
|
346 |
|
347 # Restore the slice methods, or the tests will fail with regrtest -R. |
|
348 AllTests.__getslice__ = getslice |
|
349 AllTests.__setslice__ = setslice |
|
350 AllTests.__delslice__ = delslice |
|
351 |
|
352 |
|
353 def testUnaryOps(self): |
|
354 testme = AllTests() |
|
355 |
|
356 callLst[:] = [] |
|
357 -testme |
|
358 self.assertCallStack([('__neg__', (testme,))]) |
|
359 callLst[:] = [] |
|
360 +testme |
|
361 self.assertCallStack([('__pos__', (testme,))]) |
|
362 callLst[:] = [] |
|
363 abs(testme) |
|
364 self.assertCallStack([('__abs__', (testme,))]) |
|
365 callLst[:] = [] |
|
366 int(testme) |
|
367 self.assertCallStack([('__int__', (testme,))]) |
|
368 callLst[:] = [] |
|
369 long(testme) |
|
370 self.assertCallStack([('__long__', (testme,))]) |
|
371 callLst[:] = [] |
|
372 float(testme) |
|
373 self.assertCallStack([('__float__', (testme,))]) |
|
374 callLst[:] = [] |
|
375 oct(testme) |
|
376 self.assertCallStack([('__oct__', (testme,))]) |
|
377 callLst[:] = [] |
|
378 hex(testme) |
|
379 self.assertCallStack([('__hex__', (testme,))]) |
|
380 |
|
381 |
|
382 def testMisc(self): |
|
383 testme = AllTests() |
|
384 |
|
385 callLst[:] = [] |
|
386 hash(testme) |
|
387 self.assertCallStack([('__hash__', (testme,))]) |
|
388 |
|
389 callLst[:] = [] |
|
390 repr(testme) |
|
391 self.assertCallStack([('__repr__', (testme,))]) |
|
392 |
|
393 callLst[:] = [] |
|
394 str(testme) |
|
395 self.assertCallStack([('__str__', (testme,))]) |
|
396 |
|
397 callLst[:] = [] |
|
398 testme == 1 |
|
399 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
|
400 |
|
401 callLst[:] = [] |
|
402 testme < 1 |
|
403 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
|
404 |
|
405 callLst[:] = [] |
|
406 testme > 1 |
|
407 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
|
408 |
|
409 callLst[:] = [] |
|
410 testme <> 1 # XXX kill this in py3k |
|
411 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
|
412 |
|
413 callLst[:] = [] |
|
414 testme != 1 |
|
415 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
|
416 |
|
417 callLst[:] = [] |
|
418 1 == testme |
|
419 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
|
420 |
|
421 callLst[:] = [] |
|
422 1 < testme |
|
423 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
|
424 |
|
425 callLst[:] = [] |
|
426 1 > testme |
|
427 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
|
428 |
|
429 callLst[:] = [] |
|
430 1 <> testme |
|
431 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
|
432 |
|
433 callLst[:] = [] |
|
434 1 != testme |
|
435 self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
|
436 |
|
437 |
|
438 def testGetSetAndDel(self): |
|
439 # Interfering tests |
|
440 class ExtraTests(AllTests): |
|
441 @trackCall |
|
442 def __getattr__(self, *args): |
|
443 return "SomeVal" |
|
444 |
|
445 @trackCall |
|
446 def __setattr__(self, *args): |
|
447 pass |
|
448 |
|
449 @trackCall |
|
450 def __delattr__(self, *args): |
|
451 pass |
|
452 |
|
453 testme = ExtraTests() |
|
454 |
|
455 callLst[:] = [] |
|
456 testme.spam |
|
457 self.assertCallStack([('__getattr__', (testme, "spam"))]) |
|
458 |
|
459 callLst[:] = [] |
|
460 testme.eggs = "spam, spam, spam and ham" |
|
461 self.assertCallStack([('__setattr__', (testme, "eggs", |
|
462 "spam, spam, spam and ham"))]) |
|
463 |
|
464 callLst[:] = [] |
|
465 del testme.cardinal |
|
466 self.assertCallStack([('__delattr__', (testme, "cardinal"))]) |
|
467 |
|
468 def testDel(self): |
|
469 x = [] |
|
470 |
|
471 class DelTest: |
|
472 def __del__(self): |
|
473 x.append("crab people, crab people") |
|
474 testme = DelTest() |
|
475 del testme |
|
476 import gc |
|
477 gc.collect() |
|
478 self.assertEquals(["crab people, crab people"], x) |
|
479 |
|
480 def testBadTypeReturned(self): |
|
481 # return values of some method are type-checked |
|
482 class BadTypeClass: |
|
483 def __int__(self): |
|
484 return None |
|
485 __float__ = __int__ |
|
486 __long__ = __int__ |
|
487 __str__ = __int__ |
|
488 __repr__ = __int__ |
|
489 __oct__ = __int__ |
|
490 __hex__ = __int__ |
|
491 |
|
492 for f in [int, float, long, str, repr, oct, hex]: |
|
493 self.assertRaises(TypeError, f, BadTypeClass()) |
|
494 |
|
495 def testMixIntsAndLongs(self): |
|
496 # mixing up ints and longs is okay |
|
497 class IntLongMixClass: |
|
498 @trackCall |
|
499 def __int__(self): |
|
500 return 42L |
|
501 |
|
502 @trackCall |
|
503 def __long__(self): |
|
504 return 64 |
|
505 |
|
506 mixIntAndLong = IntLongMixClass() |
|
507 |
|
508 callLst[:] = [] |
|
509 as_int = int(mixIntAndLong) |
|
510 self.assertEquals(type(as_int), long) |
|
511 self.assertEquals(as_int, 42L) |
|
512 self.assertCallStack([('__int__', (mixIntAndLong,))]) |
|
513 |
|
514 callLst[:] = [] |
|
515 as_long = long(mixIntAndLong) |
|
516 self.assertEquals(type(as_long), int) |
|
517 self.assertEquals(as_long, 64) |
|
518 self.assertCallStack([('__long__', (mixIntAndLong,))]) |
|
519 |
|
520 def testHashStuff(self): |
|
521 # Test correct errors from hash() on objects with comparisons but |
|
522 # no __hash__ |
|
523 |
|
524 class C0: |
|
525 pass |
|
526 |
|
527 hash(C0()) # This should work; the next two should raise TypeError |
|
528 |
|
529 class C1: |
|
530 def __cmp__(self, other): return 0 |
|
531 |
|
532 self.assertRaises(TypeError, hash, C1()) |
|
533 |
|
534 class C2: |
|
535 def __eq__(self, other): return 1 |
|
536 |
|
537 self.assertRaises(TypeError, hash, C2()) |
|
538 |
|
539 |
|
540 def testSFBug532646(self): |
|
541 # Test for SF bug 532646 |
|
542 |
|
543 class A: |
|
544 pass |
|
545 A.__call__ = A() |
|
546 a = A() |
|
547 |
|
548 try: |
|
549 a() # This should not segfault |
|
550 except RuntimeError: |
|
551 pass |
|
552 else: |
|
553 self.fail("Failed to raise RuntimeError") |
|
554 |
|
555 def testForExceptionsRaisedInInstanceGetattr2(self): |
|
556 # Tests for exceptions raised in instance_getattr2(). |
|
557 |
|
558 def booh(self): |
|
559 raise AttributeError("booh") |
|
560 |
|
561 class A: |
|
562 a = property(booh) |
|
563 try: |
|
564 A().a # Raised AttributeError: A instance has no attribute 'a' |
|
565 except AttributeError, x: |
|
566 if str(x) != "booh": |
|
567 self.fail("attribute error for A().a got masked: %s" % x) |
|
568 |
|
569 class E: |
|
570 __eq__ = property(booh) |
|
571 E() == E() # In debug mode, caused a C-level assert() to fail |
|
572 |
|
573 class I: |
|
574 __init__ = property(booh) |
|
575 try: |
|
576 # In debug mode, printed XXX undetected error and |
|
577 # raises AttributeError |
|
578 I() |
|
579 except AttributeError, x: |
|
580 pass |
|
581 else: |
|
582 self.fail("attribute error for I.__init__ got masked") |
|
583 |
|
584 def testHashComparisonOfMethods(self): |
|
585 # Test comparison and hash of methods |
|
586 class A: |
|
587 def __init__(self, x): |
|
588 self.x = x |
|
589 def f(self): |
|
590 pass |
|
591 def g(self): |
|
592 pass |
|
593 def __eq__(self, other): |
|
594 return self.x == other.x |
|
595 def __hash__(self): |
|
596 return self.x |
|
597 class B(A): |
|
598 pass |
|
599 |
|
600 a1 = A(1) |
|
601 a2 = A(2) |
|
602 self.assertEquals(a1.f, a1.f) |
|
603 self.assertNotEquals(a1.f, a2.f) |
|
604 self.assertNotEquals(a1.f, a1.g) |
|
605 self.assertEquals(a1.f, A(1).f) |
|
606 self.assertEquals(hash(a1.f), hash(a1.f)) |
|
607 self.assertEquals(hash(a1.f), hash(A(1).f)) |
|
608 |
|
609 self.assertNotEquals(A.f, a1.f) |
|
610 self.assertNotEquals(A.f, A.g) |
|
611 self.assertEquals(B.f, A.f) |
|
612 self.assertEquals(hash(B.f), hash(A.f)) |
|
613 |
|
614 # the following triggers a SystemError in 2.4 |
|
615 a = A(hash(A.f.im_func)^(-1)) |
|
616 hash(a.f) |
|
617 |
|
618 def test_main(): |
|
619 test_support.run_unittest(ClassTests) |
|
620 |
|
621 if __name__=='__main__': |
|
622 test_main() |