move sections
[python/dscho.git] / Lib / test / test_gc.py
blob5a35dec8c18c49c7556b830a058bdd5874f55344
1 import unittest
2 from test.test_support import verbose, run_unittest
3 import sys
4 import gc
5 import weakref
7 ### Support code
8 ###############################################################################
10 # Bug 1055820 has several tests of longstanding bugs involving weakrefs and
11 # cyclic gc.
13 # An instance of C1055820 has a self-loop, so becomes cyclic trash when
14 # unreachable.
15 class C1055820(object):
16 def __init__(self, i):
17 self.i = i
18 self.loop = self
20 class GC_Detector(object):
21 # Create an instance I. Then gc hasn't happened again so long as
22 # I.gc_happened is false.
24 def __init__(self):
25 self.gc_happened = False
27 def it_happened(ignored):
28 self.gc_happened = True
30 # Create a piece of cyclic trash that triggers it_happened when
31 # gc collects it.
32 self.wr = weakref.ref(C1055820(666), it_happened)
35 ### Tests
36 ###############################################################################
38 class GCTests(unittest.TestCase):
39 def test_list(self):
40 l = []
41 l.append(l)
42 gc.collect()
43 del l
44 self.assertEqual(gc.collect(), 1)
46 def test_dict(self):
47 d = {}
48 d[1] = d
49 gc.collect()
50 del d
51 self.assertEqual(gc.collect(), 1)
53 def test_tuple(self):
54 # since tuples are immutable we close the loop with a list
55 l = []
56 t = (l,)
57 l.append(t)
58 gc.collect()
59 del t
60 del l
61 self.assertEqual(gc.collect(), 2)
63 def test_class(self):
64 class A:
65 pass
66 A.a = A
67 gc.collect()
68 del A
69 self.assertNotEqual(gc.collect(), 0)
71 def test_newstyleclass(self):
72 class A(object):
73 pass
74 gc.collect()
75 del A
76 self.assertNotEqual(gc.collect(), 0)
78 def test_instance(self):
79 class A:
80 pass
81 a = A()
82 a.a = a
83 gc.collect()
84 del a
85 self.assertNotEqual(gc.collect(), 0)
87 def test_newinstance(self):
88 class A(object):
89 pass
90 a = A()
91 a.a = a
92 gc.collect()
93 del a
94 self.assertNotEqual(gc.collect(), 0)
95 class B(list):
96 pass
97 class C(B, A):
98 pass
99 a = C()
100 a.a = a
101 gc.collect()
102 del a
103 self.assertNotEqual(gc.collect(), 0)
104 del B, C
105 self.assertNotEqual(gc.collect(), 0)
106 A.a = A()
107 del A
108 self.assertNotEqual(gc.collect(), 0)
109 self.assertEqual(gc.collect(), 0)
111 def test_method(self):
112 # Tricky: self.__init__ is a bound method, it references the instance.
113 class A:
114 def __init__(self):
115 self.init = self.__init__
116 a = A()
117 gc.collect()
118 del a
119 self.assertNotEqual(gc.collect(), 0)
121 def test_finalizer(self):
122 # A() is uncollectable if it is part of a cycle, make sure it shows up
123 # in gc.garbage.
124 class A:
125 def __del__(self): pass
126 class B:
127 pass
128 a = A()
129 a.a = a
130 id_a = id(a)
131 b = B()
132 b.b = b
133 gc.collect()
134 del a
135 del b
136 self.assertNotEqual(gc.collect(), 0)
137 for obj in gc.garbage:
138 if id(obj) == id_a:
139 del obj.a
140 break
141 else:
142 self.fail("didn't find obj in garbage (finalizer)")
143 gc.garbage.remove(obj)
145 def test_finalizer_newclass(self):
146 # A() is uncollectable if it is part of a cycle, make sure it shows up
147 # in gc.garbage.
148 class A(object):
149 def __del__(self): pass
150 class B(object):
151 pass
152 a = A()
153 a.a = a
154 id_a = id(a)
155 b = B()
156 b.b = b
157 gc.collect()
158 del a
159 del b
160 self.assertNotEqual(gc.collect(), 0)
161 for obj in gc.garbage:
162 if id(obj) == id_a:
163 del obj.a
164 break
165 else:
166 self.fail("didn't find obj in garbage (finalizer)")
167 gc.garbage.remove(obj)
169 def test_function(self):
170 # Tricky: f -> d -> f, code should call d.clear() after the exec to
171 # break the cycle.
172 d = {}
173 exec("def f(): pass\n") in d
174 gc.collect()
175 del d
176 self.assertEqual(gc.collect(), 2)
178 def test_frame(self):
179 def f():
180 frame = sys._getframe()
181 gc.collect()
183 self.assertEqual(gc.collect(), 1)
185 def test_saveall(self):
186 # Verify that cyclic garbage like lists show up in gc.garbage if the
187 # SAVEALL option is enabled.
189 # First make sure we don't save away other stuff that just happens to
190 # be waiting for collection.
191 gc.collect()
192 # if this fails, someone else created immortal trash
193 self.assertEqual(gc.garbage, [])
195 L = []
196 L.append(L)
197 id_L = id(L)
199 debug = gc.get_debug()
200 gc.set_debug(debug | gc.DEBUG_SAVEALL)
201 del L
202 gc.collect()
203 gc.set_debug(debug)
205 self.assertEqual(len(gc.garbage), 1)
206 obj = gc.garbage.pop()
207 self.assertEqual(id(obj), id_L)
209 def test_del(self):
210 # __del__ methods can trigger collection, make this to happen
211 thresholds = gc.get_threshold()
212 gc.enable()
213 gc.set_threshold(1)
215 class A:
216 def __del__(self):
217 dir(self)
218 a = A()
219 del a
221 gc.disable()
222 gc.set_threshold(*thresholds)
224 def test_del_newclass(self):
225 # __del__ methods can trigger collection, make this to happen
226 thresholds = gc.get_threshold()
227 gc.enable()
228 gc.set_threshold(1)
230 class A(object):
231 def __del__(self):
232 dir(self)
233 a = A()
234 del a
236 gc.disable()
237 gc.set_threshold(*thresholds)
239 # The following two tests are fragile:
240 # They precisely count the number of allocations,
241 # which is highly implementation-dependent.
242 # For example:
243 # - disposed tuples are not freed, but reused
244 # - the call to assertEqual somehow avoids building its args tuple
245 def test_get_count(self):
246 # Avoid future allocation of method object
247 assertEqual = self._baseAssertEqual
248 gc.collect()
249 assertEqual(gc.get_count(), (0, 0, 0))
250 a = dict()
251 # since gc.collect(), we created two objects:
252 # the dict, and the tuple returned by get_count()
253 assertEqual(gc.get_count(), (2, 0, 0))
255 def test_collect_generations(self):
256 # Avoid future allocation of method object
257 assertEqual = self.assertEqual
258 gc.collect()
259 a = dict()
260 gc.collect(0)
261 assertEqual(gc.get_count(), (0, 1, 0))
262 gc.collect(1)
263 assertEqual(gc.get_count(), (0, 0, 1))
264 gc.collect(2)
265 assertEqual(gc.get_count(), (0, 0, 0))
267 def test_trashcan(self):
268 class Ouch:
269 n = 0
270 def __del__(self):
271 Ouch.n = Ouch.n + 1
272 if Ouch.n % 17 == 0:
273 gc.collect()
275 # "trashcan" is a hack to prevent stack overflow when deallocating
276 # very deeply nested tuples etc. It works in part by abusing the
277 # type pointer and refcount fields, and that can yield horrible
278 # problems when gc tries to traverse the structures.
279 # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
280 # most likely die via segfault.
282 # Note: In 2.3 the possibility for compiling without cyclic gc was
283 # removed, and that in turn allows the trashcan mechanism to work
284 # via much simpler means (e.g., it never abuses the type pointer or
285 # refcount fields anymore). Since it's much less likely to cause a
286 # problem now, the various constants in this expensive (we force a lot
287 # of full collections) test are cut back from the 2.2 version.
288 gc.enable()
289 N = 150
290 for count in range(2):
291 t = []
292 for i in range(N):
293 t = [t, Ouch()]
294 u = []
295 for i in range(N):
296 u = [u, Ouch()]
297 v = {}
298 for i in range(N):
299 v = {1: v, 2: Ouch()}
300 gc.disable()
302 def test_boom(self):
303 class Boom:
304 def __getattr__(self, someattribute):
305 del self.attr
306 raise AttributeError
308 a = Boom()
309 b = Boom()
310 a.attr = b
311 b.attr = a
313 gc.collect()
314 garbagelen = len(gc.garbage)
315 del a, b
316 # a<->b are in a trash cycle now. Collection will invoke
317 # Boom.__getattr__ (to see whether a and b have __del__ methods), and
318 # __getattr__ deletes the internal "attr" attributes as a side effect.
319 # That causes the trash cycle to get reclaimed via refcounts falling to
320 # 0, thus mutating the trash graph as a side effect of merely asking
321 # whether __del__ exists. This used to (before 2.3b1) crash Python.
322 # Now __getattr__ isn't called.
323 self.assertEqual(gc.collect(), 4)
324 self.assertEqual(len(gc.garbage), garbagelen)
326 def test_boom2(self):
327 class Boom2:
328 def __init__(self):
329 self.x = 0
331 def __getattr__(self, someattribute):
332 self.x += 1
333 if self.x > 1:
334 del self.attr
335 raise AttributeError
337 a = Boom2()
338 b = Boom2()
339 a.attr = b
340 b.attr = a
342 gc.collect()
343 garbagelen = len(gc.garbage)
344 del a, b
345 # Much like test_boom(), except that __getattr__ doesn't break the
346 # cycle until the second time gc checks for __del__. As of 2.3b1,
347 # there isn't a second time, so this simply cleans up the trash cycle.
348 # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
349 # reclaimed this way.
350 self.assertEqual(gc.collect(), 4)
351 self.assertEqual(len(gc.garbage), garbagelen)
353 def test_boom_new(self):
354 # boom__new and boom2_new are exactly like boom and boom2, except use
355 # new-style classes.
357 class Boom_New(object):
358 def __getattr__(self, someattribute):
359 del self.attr
360 raise AttributeError
362 a = Boom_New()
363 b = Boom_New()
364 a.attr = b
365 b.attr = a
367 gc.collect()
368 garbagelen = len(gc.garbage)
369 del a, b
370 self.assertEqual(gc.collect(), 4)
371 self.assertEqual(len(gc.garbage), garbagelen)
373 def test_boom2_new(self):
374 class Boom2_New(object):
375 def __init__(self):
376 self.x = 0
378 def __getattr__(self, someattribute):
379 self.x += 1
380 if self.x > 1:
381 del self.attr
382 raise AttributeError
384 a = Boom2_New()
385 b = Boom2_New()
386 a.attr = b
387 b.attr = a
389 gc.collect()
390 garbagelen = len(gc.garbage)
391 del a, b
392 self.assertEqual(gc.collect(), 4)
393 self.assertEqual(len(gc.garbage), garbagelen)
395 def test_get_referents(self):
396 alist = [1, 3, 5]
397 got = gc.get_referents(alist)
398 got.sort()
399 self.assertEqual(got, alist)
401 atuple = tuple(alist)
402 got = gc.get_referents(atuple)
403 got.sort()
404 self.assertEqual(got, alist)
406 adict = {1: 3, 5: 7}
407 expected = [1, 3, 5, 7]
408 got = gc.get_referents(adict)
409 got.sort()
410 self.assertEqual(got, expected)
412 got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
413 got.sort()
414 self.assertEqual(got, [0, 0] + range(5))
416 self.assertEqual(gc.get_referents(1, 'a', 4j), [])
418 def test_is_tracked(self):
419 # Atomic built-in types are not tracked, user-defined objects and
420 # mutable containers are.
421 # NOTE: types with special optimizations (e.g. tuple) have tests
422 # in their own test files instead.
423 self.assertFalse(gc.is_tracked(None))
424 self.assertFalse(gc.is_tracked(1))
425 self.assertFalse(gc.is_tracked(1.0))
426 self.assertFalse(gc.is_tracked(1.0 + 5.0j))
427 self.assertFalse(gc.is_tracked(True))
428 self.assertFalse(gc.is_tracked(False))
429 self.assertFalse(gc.is_tracked("a"))
430 self.assertFalse(gc.is_tracked(u"a"))
431 self.assertFalse(gc.is_tracked(bytearray("a")))
432 self.assertFalse(gc.is_tracked(type))
433 self.assertFalse(gc.is_tracked(int))
434 self.assertFalse(gc.is_tracked(object))
435 self.assertFalse(gc.is_tracked(object()))
437 class OldStyle:
438 pass
439 class NewStyle(object):
440 pass
441 self.assertTrue(gc.is_tracked(gc))
442 self.assertTrue(gc.is_tracked(OldStyle))
443 self.assertTrue(gc.is_tracked(OldStyle()))
444 self.assertTrue(gc.is_tracked(NewStyle))
445 self.assertTrue(gc.is_tracked(NewStyle()))
446 self.assertTrue(gc.is_tracked([]))
447 self.assertTrue(gc.is_tracked(set()))
449 def test_bug1055820b(self):
450 # Corresponds to temp2b.py in the bug report.
452 ouch = []
453 def callback(ignored):
454 ouch[:] = [wr() for wr in WRs]
456 Cs = [C1055820(i) for i in range(2)]
457 WRs = [weakref.ref(c, callback) for c in Cs]
458 c = None
460 gc.collect()
461 self.assertEqual(len(ouch), 0)
462 # Make the two instances trash, and collect again. The bug was that
463 # the callback materialized a strong reference to an instance, but gc
464 # cleared the instance's dict anyway.
465 Cs = None
466 gc.collect()
467 self.assertEqual(len(ouch), 2) # else the callbacks didn't run
468 for x in ouch:
469 # If the callback resurrected one of these guys, the instance
470 # would be damaged, with an empty __dict__.
471 self.assertEqual(x, None)
473 class GCTogglingTests(unittest.TestCase):
474 def setUp(self):
475 gc.enable()
477 def tearDown(self):
478 gc.disable()
480 def test_bug1055820c(self):
481 # Corresponds to temp2c.py in the bug report. This is pretty
482 # elaborate.
484 c0 = C1055820(0)
485 # Move c0 into generation 2.
486 gc.collect()
488 c1 = C1055820(1)
489 c1.keep_c0_alive = c0
490 del c0.loop # now only c1 keeps c0 alive
492 c2 = C1055820(2)
493 c2wr = weakref.ref(c2) # no callback!
495 ouch = []
496 def callback(ignored):
497 ouch[:] = [c2wr()]
499 # The callback gets associated with a wr on an object in generation 2.
500 c0wr = weakref.ref(c0, callback)
502 c0 = c1 = c2 = None
504 # What we've set up: c0, c1, and c2 are all trash now. c0 is in
505 # generation 2. The only thing keeping it alive is that c1 points to
506 # it. c1 and c2 are in generation 0, and are in self-loops. There's a
507 # global weakref to c2 (c2wr), but that weakref has no callback.
508 # There's also a global weakref to c0 (c0wr), and that does have a
509 # callback, and that callback references c2 via c2wr().
511 # c0 has a wr with callback, which references c2wr
514 # | Generation 2 above dots
515 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
516 # | Generation 0 below dots
519 # ^->c1 ^->c2 has a wr but no callback
520 # | | | |
521 # <--v <--v
523 # So this is the nightmare: when generation 0 gets collected, we see
524 # that c2 has a callback-free weakref, and c1 doesn't even have a
525 # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
526 # the only object that has a weakref with a callback. gc clears c1
527 # and c2. Clearing c1 has the side effect of dropping the refcount on
528 # c0 to 0, so c0 goes away (despite that it's in an older generation)
529 # and c0's wr callback triggers. That in turn materializes a reference
530 # to c2 via c2wr(), but c2 gets cleared anyway by gc.
532 # We want to let gc happen "naturally", to preserve the distinction
533 # between generations.
534 junk = []
535 i = 0
536 detector = GC_Detector()
537 while not detector.gc_happened:
538 i += 1
539 if i > 10000:
540 self.fail("gc didn't happen after 10000 iterations")
541 self.assertEqual(len(ouch), 0)
542 junk.append([]) # this will eventually trigger gc
544 self.assertEqual(len(ouch), 1) # else the callback wasn't invoked
545 for x in ouch:
546 # If the callback resurrected c2, the instance would be damaged,
547 # with an empty __dict__.
548 self.assertEqual(x, None)
550 def test_bug1055820d(self):
551 # Corresponds to temp2d.py in the bug report. This is very much like
552 # test_bug1055820c, but uses a __del__ method instead of a weakref
553 # callback to sneak in a resurrection of cyclic trash.
555 ouch = []
556 class D(C1055820):
557 def __del__(self):
558 ouch[:] = [c2wr()]
560 d0 = D(0)
561 # Move all the above into generation 2.
562 gc.collect()
564 c1 = C1055820(1)
565 c1.keep_d0_alive = d0
566 del d0.loop # now only c1 keeps d0 alive
568 c2 = C1055820(2)
569 c2wr = weakref.ref(c2) # no callback!
571 d0 = c1 = c2 = None
573 # What we've set up: d0, c1, and c2 are all trash now. d0 is in
574 # generation 2. The only thing keeping it alive is that c1 points to
575 # it. c1 and c2 are in generation 0, and are in self-loops. There's
576 # a global weakref to c2 (c2wr), but that weakref has no callback.
577 # There are no other weakrefs.
579 # d0 has a __del__ method that references c2wr
582 # | Generation 2 above dots
583 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
584 # | Generation 0 below dots
587 # ^->c1 ^->c2 has a wr but no callback
588 # | | | |
589 # <--v <--v
591 # So this is the nightmare: when generation 0 gets collected, we see
592 # that c2 has a callback-free weakref, and c1 doesn't even have a
593 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
594 # c1 and c2. Clearing c1 has the side effect of dropping the refcount
595 # on d0 to 0, so d0 goes away (despite that it's in an older
596 # generation) and d0's __del__ triggers. That in turn materializes
597 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
599 # We want to let gc happen "naturally", to preserve the distinction
600 # between generations.
601 detector = GC_Detector()
602 junk = []
603 i = 0
604 while not detector.gc_happened:
605 i += 1
606 if i > 10000:
607 self.fail("gc didn't happen after 10000 iterations")
608 self.assertEqual(len(ouch), 0)
609 junk.append([]) # this will eventually trigger gc
611 self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked
612 for x in ouch:
613 # If __del__ resurrected c2, the instance would be damaged, with an
614 # empty __dict__.
615 self.assertEqual(x, None)
617 def test_main():
618 enabled = gc.isenabled()
619 gc.disable()
620 assert not gc.isenabled()
621 debug = gc.get_debug()
622 gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
624 try:
625 gc.collect() # Delete 2nd generation garbage
626 run_unittest(GCTests, GCTogglingTests)
627 finally:
628 gc.set_debug(debug)
629 # test gc.enable() even if GC is disabled by default
630 if verbose:
631 print "restoring automatic collection"
632 # make sure to always test gc.enable()
633 gc.enable()
634 assert gc.isenabled()
635 if not enabled:
636 gc.disable()
638 if __name__ == "__main__":
639 test_main()