• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2from test.test_support import verbose, run_unittest
3import sys
4import gc
5import weakref
6
7### Support code
8###############################################################################
9
10# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
11# cyclic gc.
12
13# An instance of C1055820 has a self-loop, so becomes cyclic trash when
14# unreachable.
15class C1055820(object):
16    def __init__(self, i):
17        self.i = i
18        self.loop = self
19
20class GC_Detector(object):
21    # Create an instance I.  Then gc hasn't happened again so long as
22    # I.gc_happened is false.
23
24    def __init__(self):
25        self.gc_happened = False
26
27        def it_happened(ignored):
28            self.gc_happened = True
29
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)
33
34
35### Tests
36###############################################################################
37
38class 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)
45
46    def test_dict(self):
47        d = {}
48        d[1] = d
49        gc.collect()
50        del d
51        self.assertEqual(gc.collect(), 1)
52
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)
62
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)
70
71    def test_newstyleclass(self):
72        class A(object):
73            pass
74        gc.collect()
75        del A
76        self.assertNotEqual(gc.collect(), 0)
77
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)
86
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)
110
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)
120
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)
144
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)
168
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)
177
178    def test_frame(self):
179        def f():
180            frame = sys._getframe()
181        gc.collect()
182        f()
183        self.assertEqual(gc.collect(), 1)
184
185    def test_saveall(self):
186        # Verify that cyclic garbage like lists show up in gc.garbage if the
187        # SAVEALL option is enabled.
188
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, [])
194
195        L = []
196        L.append(L)
197        id_L = id(L)
198
199        debug = gc.get_debug()
200        gc.set_debug(debug | gc.DEBUG_SAVEALL)
201        del L
202        gc.collect()
203        gc.set_debug(debug)
204
205        self.assertEqual(len(gc.garbage), 1)
206        obj = gc.garbage.pop()
207        self.assertEqual(id(obj), id_L)
208
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)
214
215        class A:
216            def __del__(self):
217                dir(self)
218        a = A()
219        del a
220
221        gc.disable()
222        gc.set_threshold(*thresholds)
223
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)
229
230        class A(object):
231            def __del__(self):
232                dir(self)
233        a = A()
234        del a
235
236        gc.disable()
237        gc.set_threshold(*thresholds)
238
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))
254
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))
266
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()
274
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.
281
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()
301
302    def test_boom(self):
303        class Boom:
304            def __getattr__(self, someattribute):
305                del self.attr
306                raise AttributeError
307
308        a = Boom()
309        b = Boom()
310        a.attr = b
311        b.attr = a
312
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)
325
326    def test_boom2(self):
327        class Boom2:
328            def __init__(self):
329                self.x = 0
330
331            def __getattr__(self, someattribute):
332                self.x += 1
333                if self.x > 1:
334                    del self.attr
335                raise AttributeError
336
337        a = Boom2()
338        b = Boom2()
339        a.attr = b
340        b.attr = a
341
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)
352
353    def test_boom_new(self):
354        # boom__new and boom2_new are exactly like boom and boom2, except use
355        # new-style classes.
356
357        class Boom_New(object):
358            def __getattr__(self, someattribute):
359                del self.attr
360                raise AttributeError
361
362        a = Boom_New()
363        b = Boom_New()
364        a.attr = b
365        b.attr = a
366
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)
372
373    def test_boom2_new(self):
374        class Boom2_New(object):
375            def __init__(self):
376                self.x = 0
377
378            def __getattr__(self, someattribute):
379                self.x += 1
380                if self.x > 1:
381                    del self.attr
382                raise AttributeError
383
384        a = Boom2_New()
385        b = Boom2_New()
386        a.attr = b
387        b.attr = a
388
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)
394
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)
400
401        atuple = tuple(alist)
402        got = gc.get_referents(atuple)
403        got.sort()
404        self.assertEqual(got, alist)
405
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)
411
412        got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
413        got.sort()
414        self.assertEqual(got, [0, 0] + range(5))
415
416        self.assertEqual(gc.get_referents(1, 'a', 4j), [])
417
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()))
436
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()))
448
449    def test_bug1055820b(self):
450        # Corresponds to temp2b.py in the bug report.
451
452        ouch = []
453        def callback(ignored):
454            ouch[:] = [wr() for wr in WRs]
455
456        Cs = [C1055820(i) for i in range(2)]
457        WRs = [weakref.ref(c, callback) for c in Cs]
458        c = None
459
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)
472
473class GCTogglingTests(unittest.TestCase):
474    def setUp(self):
475        gc.enable()
476
477    def tearDown(self):
478        gc.disable()
479
480    def test_bug1055820c(self):
481        # Corresponds to temp2c.py in the bug report.  This is pretty
482        # elaborate.
483
484        c0 = C1055820(0)
485        # Move c0 into generation 2.
486        gc.collect()
487
488        c1 = C1055820(1)
489        c1.keep_c0_alive = c0
490        del c0.loop # now only c1 keeps c0 alive
491
492        c2 = C1055820(2)
493        c2wr = weakref.ref(c2) # no callback!
494
495        ouch = []
496        def callback(ignored):
497            ouch[:] = [c2wr()]
498
499        # The callback gets associated with a wr on an object in generation 2.
500        c0wr = weakref.ref(c0, callback)
501
502        c0 = c1 = c2 = None
503
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().
510        #
511        #               c0 has a wr with callback, which references c2wr
512        #               ^
513        #               |
514        #               |     Generation 2 above dots
515        #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
516        #               |     Generation 0 below dots
517        #               |
518        #               |
519        #            ^->c1   ^->c2 has a wr but no callback
520        #            |  |    |  |
521        #            <--v    <--v
522        #
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.
531
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
543
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)
549
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.
554
555        ouch = []
556        class D(C1055820):
557            def __del__(self):
558                ouch[:] = [c2wr()]
559
560        d0 = D(0)
561        # Move all the above into generation 2.
562        gc.collect()
563
564        c1 = C1055820(1)
565        c1.keep_d0_alive = d0
566        del d0.loop # now only c1 keeps d0 alive
567
568        c2 = C1055820(2)
569        c2wr = weakref.ref(c2) # no callback!
570
571        d0 = c1 = c2 = None
572
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.
578        #
579        #               d0 has a __del__ method that references c2wr
580        #               ^
581        #               |
582        #               |     Generation 2 above dots
583        #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
584        #               |     Generation 0 below dots
585        #               |
586        #               |
587        #            ^->c1   ^->c2 has a wr but no callback
588        #            |  |    |  |
589        #            <--v    <--v
590        #
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.
598
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
610
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)
616
617def 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
623
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()
637
638if __name__ == "__main__":
639    test_main()
640