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