PyXR

c:\python24\lib \ test \ test_gc.py



0001 from test.test_support import verify, verbose, TestFailed, vereq
0002 import sys
0003 import gc
0004 import weakref
0005 
0006 def expect(actual, expected, name):
0007     if actual != expected:
0008         raise TestFailed, "test_%s: actual %r, expected %r" % (
0009             name, actual, expected)
0010 
0011 def expect_nonzero(actual, name):
0012     if actual == 0:
0013         raise TestFailed, "test_%s: unexpected zero" % name
0014 
0015 def run_test(name, thunk):
0016     if verbose:
0017         print "testing %s..." % name,
0018     thunk()
0019     if verbose:
0020         print "ok"
0021 
0022 def test_list():
0023     l = []
0024     l.append(l)
0025     gc.collect()
0026     del l
0027     expect(gc.collect(), 1, "list")
0028 
0029 def test_dict():
0030     d = {}
0031     d[1] = d
0032     gc.collect()
0033     del d
0034     expect(gc.collect(), 1, "dict")
0035 
0036 def test_tuple():
0037     # since tuples are immutable we close the loop with a list
0038     l = []
0039     t = (l,)
0040     l.append(t)
0041     gc.collect()
0042     del t
0043     del l
0044     expect(gc.collect(), 2, "tuple")
0045 
0046 def test_class():
0047     class A:
0048         pass
0049     A.a = A
0050     gc.collect()
0051     del A
0052     expect_nonzero(gc.collect(), "class")
0053 
0054 def test_newstyleclass():
0055     class A(object):
0056         pass
0057     gc.collect()
0058     del A
0059     expect_nonzero(gc.collect(), "staticclass")
0060 
0061 def test_instance():
0062     class A:
0063         pass
0064     a = A()
0065     a.a = a
0066     gc.collect()
0067     del a
0068     expect_nonzero(gc.collect(), "instance")
0069 
0070 def test_newinstance():
0071     class A(object):
0072         pass
0073     a = A()
0074     a.a = a
0075     gc.collect()
0076     del a
0077     expect_nonzero(gc.collect(), "newinstance")
0078     class B(list):
0079         pass
0080     class C(B, A):
0081         pass
0082     a = C()
0083     a.a = a
0084     gc.collect()
0085     del a
0086     expect_nonzero(gc.collect(), "newinstance(2)")
0087     del B, C
0088     expect_nonzero(gc.collect(), "newinstance(3)")
0089     A.a = A()
0090     del A
0091     expect_nonzero(gc.collect(), "newinstance(4)")
0092     expect(gc.collect(), 0, "newinstance(5)")
0093 
0094 def test_method():
0095     # Tricky: self.__init__ is a bound method, it references the instance.
0096     class A:
0097         def __init__(self):
0098             self.init = self.__init__
0099     a = A()
0100     gc.collect()
0101     del a
0102     expect_nonzero(gc.collect(), "method")
0103 
0104 def test_finalizer():
0105     # A() is uncollectable if it is part of a cycle, make sure it shows up
0106     # in gc.garbage.
0107     class A:
0108         def __del__(self): pass
0109     class B:
0110         pass
0111     a = A()
0112     a.a = a
0113     id_a = id(a)
0114     b = B()
0115     b.b = b
0116     gc.collect()
0117     del a
0118     del b
0119     expect_nonzero(gc.collect(), "finalizer")
0120     for obj in gc.garbage:
0121         if id(obj) == id_a:
0122             del obj.a
0123             break
0124     else:
0125         raise TestFailed, "didn't find obj in garbage (finalizer)"
0126     gc.garbage.remove(obj)
0127 
0128 def test_finalizer_newclass():
0129     # A() is uncollectable if it is part of a cycle, make sure it shows up
0130     # in gc.garbage.
0131     class A(object):
0132         def __del__(self): pass
0133     class B(object):
0134         pass
0135     a = A()
0136     a.a = a
0137     id_a = id(a)
0138     b = B()
0139     b.b = b
0140     gc.collect()
0141     del a
0142     del b
0143     expect_nonzero(gc.collect(), "finalizer")
0144     for obj in gc.garbage:
0145         if id(obj) == id_a:
0146             del obj.a
0147             break
0148     else:
0149         raise TestFailed, "didn't find obj in garbage (finalizer)"
0150     gc.garbage.remove(obj)
0151 
0152 def test_function():
0153     # Tricky: f -> d -> f, code should call d.clear() after the exec to
0154     # break the cycle.
0155     d = {}
0156     exec("def f(): pass\n") in d
0157     gc.collect()
0158     del d
0159     expect(gc.collect(), 2, "function")
0160 
0161 def test_frame():
0162     def f():
0163         frame = sys._getframe()
0164     gc.collect()
0165     f()
0166     expect(gc.collect(), 1, "frame")
0167 
0168 
0169 def test_saveall():
0170     # Verify that cyclic garbage like lists show up in gc.garbage if the
0171     # SAVEALL option is enabled.
0172 
0173     # First make sure we don't save away other stuff that just happens to
0174     # be waiting for collection.
0175     gc.collect()
0176     vereq(gc.garbage, []) # if this fails, someone else created immortal trash
0177 
0178     L = []
0179     L.append(L)
0180     id_L = id(L)
0181 
0182     debug = gc.get_debug()
0183     gc.set_debug(debug | gc.DEBUG_SAVEALL)
0184     del L
0185     gc.collect()
0186     gc.set_debug(debug)
0187 
0188     vereq(len(gc.garbage), 1)
0189     obj = gc.garbage.pop()
0190     vereq(id(obj), id_L)
0191 
0192 def test_del():
0193     # __del__ methods can trigger collection, make this to happen
0194     thresholds = gc.get_threshold()
0195     gc.enable()
0196     gc.set_threshold(1)
0197 
0198     class A:
0199         def __del__(self):
0200             dir(self)
0201     a = A()
0202     del a
0203 
0204     gc.disable()
0205     gc.set_threshold(*thresholds)
0206 
0207 def test_del_newclass():
0208     # __del__ methods can trigger collection, make this to happen
0209     thresholds = gc.get_threshold()
0210     gc.enable()
0211     gc.set_threshold(1)
0212 
0213     class A(object):
0214         def __del__(self):
0215             dir(self)
0216     a = A()
0217     del a
0218 
0219     gc.disable()
0220     gc.set_threshold(*thresholds)
0221 
0222 class Ouch:
0223     n = 0
0224     def __del__(self):
0225         Ouch.n = Ouch.n + 1
0226         if Ouch.n % 17 == 0:
0227             gc.collect()
0228 
0229 def test_trashcan():
0230     # "trashcan" is a hack to prevent stack overflow when deallocating
0231     # very deeply nested tuples etc.  It works in part by abusing the
0232     # type pointer and refcount fields, and that can yield horrible
0233     # problems when gc tries to traverse the structures.
0234     # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
0235     # most likely die via segfault.
0236 
0237     # Note:  In 2.3 the possibility for compiling without cyclic gc was
0238     # removed, and that in turn allows the trashcan mechanism to work
0239     # via much simpler means (e.g., it never abuses the type pointer or
0240     # refcount fields anymore).  Since it's much less likely to cause a
0241     # problem now, the various constants in this expensive (we force a lot
0242     # of full collections) test are cut back from the 2.2 version.
0243     gc.enable()
0244     N = 150
0245     for count in range(2):
0246         t = []
0247         for i in range(N):
0248             t = [t, Ouch()]
0249         u = []
0250         for i in range(N):
0251             u = [u, Ouch()]
0252         v = {}
0253         for i in range(N):
0254             v = {1: v, 2: Ouch()}
0255     gc.disable()
0256 
0257 class Boom:
0258     def __getattr__(self, someattribute):
0259         del self.attr
0260         raise AttributeError
0261 
0262 def test_boom():
0263     a = Boom()
0264     b = Boom()
0265     a.attr = b
0266     b.attr = a
0267 
0268     gc.collect()
0269     garbagelen = len(gc.garbage)
0270     del a, b
0271     # a<->b are in a trash cycle now.  Collection will invoke Boom.__getattr__
0272     # (to see whether a and b have __del__ methods), and __getattr__ deletes
0273     # the internal "attr" attributes as a side effect.  That causes the
0274     # trash cycle to get reclaimed via refcounts falling to 0, thus mutating
0275     # the trash graph as a side effect of merely asking whether __del__
0276     # exists.  This used to (before 2.3b1) crash Python.  Now __getattr__
0277     # isn't called.
0278     expect(gc.collect(), 4, "boom")
0279     expect(len(gc.garbage), garbagelen, "boom")
0280 
0281 class Boom2:
0282     def __init__(self):
0283         self.x = 0
0284 
0285     def __getattr__(self, someattribute):
0286         self.x += 1
0287         if self.x > 1:
0288             del self.attr
0289         raise AttributeError
0290 
0291 def test_boom2():
0292     a = Boom2()
0293     b = Boom2()
0294     a.attr = b
0295     b.attr = a
0296 
0297     gc.collect()
0298     garbagelen = len(gc.garbage)
0299     del a, b
0300     # Much like test_boom(), except that __getattr__ doesn't break the
0301     # cycle until the second time gc checks for __del__.  As of 2.3b1,
0302     # there isn't a second time, so this simply cleans up the trash cycle.
0303     # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get reclaimed
0304     # this way.
0305     expect(gc.collect(), 4, "boom2")
0306     expect(len(gc.garbage), garbagelen, "boom2")
0307 
0308 # boom__new and boom2_new are exactly like boom and boom2, except use
0309 # new-style classes.
0310 
0311 class Boom_New(object):
0312     def __getattr__(self, someattribute):
0313         del self.attr
0314         raise AttributeError
0315 
0316 def test_boom_new():
0317     a = Boom_New()
0318     b = Boom_New()
0319     a.attr = b
0320     b.attr = a
0321 
0322     gc.collect()
0323     garbagelen = len(gc.garbage)
0324     del a, b
0325     expect(gc.collect(), 4, "boom_new")
0326     expect(len(gc.garbage), garbagelen, "boom_new")
0327 
0328 class Boom2_New(object):
0329     def __init__(self):
0330         self.x = 0
0331 
0332     def __getattr__(self, someattribute):
0333         self.x += 1
0334         if self.x > 1:
0335             del self.attr
0336         raise AttributeError
0337 
0338 def test_boom2_new():
0339     a = Boom2_New()
0340     b = Boom2_New()
0341     a.attr = b
0342     b.attr = a
0343 
0344     gc.collect()
0345     garbagelen = len(gc.garbage)
0346     del a, b
0347     expect(gc.collect(), 4, "boom2_new")
0348     expect(len(gc.garbage), garbagelen, "boom2_new")
0349 
0350 def test_get_referents():
0351     alist = [1, 3, 5]
0352     got = gc.get_referents(alist)
0353     got.sort()
0354     expect(got, alist, "get_referents")
0355 
0356     atuple = tuple(alist)
0357     got = gc.get_referents(atuple)
0358     got.sort()
0359     expect(got, alist, "get_referents")
0360 
0361     adict = {1: 3, 5: 7}
0362     expected = [1, 3, 5, 7]
0363     got = gc.get_referents(adict)
0364     got.sort()
0365     expect(got, expected, "get_referents")
0366 
0367     got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
0368     got.sort()
0369     expect(got, [0, 0] + range(5), "get_referents")
0370 
0371     expect(gc.get_referents(1, 'a', 4j), [], "get_referents")
0372 
0373 # Bug 1055820 has several tests of longstanding bugs involving weakrefs and
0374 # cyclic gc.
0375 
0376 # An instance of C1055820 has a self-loop, so becomes cyclic trash when
0377 # unreachable.
0378 class C1055820(object):
0379     def __init__(self, i):
0380         self.i = i
0381         self.loop = self
0382 
0383 class GC_Detector(object):
0384     # Create an instance I.  Then gc hasn't happened again so long as
0385     # I.gc_happened is false.
0386 
0387     def __init__(self):
0388         self.gc_happened = False
0389 
0390         def it_happened(ignored):
0391             self.gc_happened = True
0392 
0393         # Create a piece of cyclic trash that triggers it_happened when
0394         # gc collects it.
0395         self.wr = weakref.ref(C1055820(666), it_happened)
0396 
0397 def test_bug1055820b():
0398     # Corresponds to temp2b.py in the bug report.
0399 
0400     ouch = []
0401     def callback(ignored):
0402         ouch[:] = [wr() for wr in WRs]
0403 
0404     Cs = [C1055820(i) for i in range(2)]
0405     WRs = [weakref.ref(c, callback) for c in Cs]
0406     c = None
0407 
0408     gc.collect()
0409     expect(len(ouch), 0, "bug1055820b")
0410     # Make the two instances trash, and collect again.  The bug was that
0411     # the callback materialized a strong reference to an instance, but gc
0412     # cleared the instance's dict anyway.
0413     Cs = None
0414     gc.collect()
0415     expect(len(ouch), 2, "bug1055820b")  # else the callbacks didn't run
0416     for x in ouch:
0417         # If the callback resurrected one of these guys, the instance
0418         # would be damaged, with an empty __dict__.
0419         expect(x, None, "bug1055820b")
0420 
0421 def test_bug1055820c():
0422     # Corresponds to temp2c.py in the bug report.  This is pretty elaborate.
0423 
0424     c0 = C1055820(0)
0425     # Move c0 into generation 2.
0426     gc.collect()
0427 
0428     c1 = C1055820(1)
0429     c1.keep_c0_alive = c0
0430     del c0.loop # now only c1 keeps c0 alive
0431 
0432     c2 = C1055820(2)
0433     c2wr = weakref.ref(c2) # no callback!
0434 
0435     ouch = []
0436     def callback(ignored):
0437         ouch[:] = [c2wr()]
0438 
0439     # The callback gets associated with a wr on an object in generation 2.
0440     c0wr = weakref.ref(c0, callback)
0441 
0442     c0 = c1 = c2 = None
0443 
0444     # What we've set up:  c0, c1, and c2 are all trash now.  c0 is in
0445     # generation 2.  The only thing keeping it alive is that c1 points to it.
0446     # c1 and c2 are in generation 0, and are in self-loops.  There's a global
0447     # weakref to c2 (c2wr), but that weakref has no callback.  There's also
0448     # a global weakref to c0 (c0wr), and that does have a callback, and that
0449     # callback references c2 via c2wr().
0450     #
0451     #               c0 has a wr with callback, which references c2wr
0452     #               ^
0453     #               |
0454     #               |     Generation 2 above dots
0455     #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
0456     #               |     Generation 0 below dots
0457     #               |
0458     #               |
0459     #            ^->c1   ^->c2 has a wr but no callback
0460     #            |  |    |  |
0461     #            <--v    <--v
0462     #
0463     # So this is the nightmare:  when generation 0 gets collected, we see that
0464     # c2 has a callback-free weakref, and c1 doesn't even have a weakref.
0465     # Collecting generation 0 doesn't see c0 at all, and c0 is the only object
0466     # that has a weakref with a callback.  gc clears c1 and c2.  Clearing c1
0467     # has the side effect of dropping the refcount on c0 to 0, so c0 goes
0468     # away (despite that it's in an older generation) and c0's wr callback
0469     # triggers.  That in turn materializes a reference to c2 via c2wr(), but
0470     # c2 gets cleared anyway by gc.
0471 
0472     # We want to let gc happen "naturally", to preserve the distinction
0473     # between generations.
0474     junk = []
0475     i = 0
0476     detector = GC_Detector()
0477     while not detector.gc_happened:
0478         i += 1
0479         if i > 10000:
0480             raise TestFailed("gc didn't happen after 10000 iterations")
0481         expect(len(ouch), 0, "bug1055820c")
0482         junk.append([])  # this will eventually trigger gc
0483 
0484     expect(len(ouch), 1, "bug1055820c")  # else the callback wasn't invoked
0485     for x in ouch:
0486         # If the callback resurrected c2, the instance would be damaged,
0487         # with an empty __dict__.
0488         expect(x, None, "bug1055820c")
0489 
0490 def test_bug1055820d():
0491     # Corresponds to temp2d.py in the bug report.  This is very much like
0492     # test_bug1055820c, but uses a __del__ method instead of a weakref
0493     # callback to sneak in a resurrection of cyclic trash.
0494 
0495     ouch = []
0496     class D(C1055820):
0497         def __del__(self):
0498             ouch[:] = [c2wr()]
0499 
0500     d0 = D(0)
0501     # Move all the above into generation 2.
0502     gc.collect()
0503 
0504     c1 = C1055820(1)
0505     c1.keep_d0_alive = d0
0506     del d0.loop # now only c1 keeps d0 alive
0507 
0508     c2 = C1055820(2)
0509     c2wr = weakref.ref(c2) # no callback!
0510 
0511     d0 = c1 = c2 = None
0512 
0513     # What we've set up:  d0, c1, and c2 are all trash now.  d0 is in
0514     # generation 2.  The only thing keeping it alive is that c1 points to it.
0515     # c1 and c2 are in generation 0, and are in self-loops.  There's a global
0516     # weakref to c2 (c2wr), but that weakref has no callback.  There are no
0517     # other weakrefs.
0518     #
0519     #               d0 has a __del__ method that references c2wr
0520     #               ^
0521     #               |
0522     #               |     Generation 2 above dots
0523     #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
0524     #               |     Generation 0 below dots
0525     #               |
0526     #               |
0527     #            ^->c1   ^->c2 has a wr but no callback
0528     #            |  |    |  |
0529     #            <--v    <--v
0530     #
0531     # So this is the nightmare:  when generation 0 gets collected, we see that
0532     # c2 has a callback-free weakref, and c1 doesn't even have a weakref.
0533     # Collecting generation 0 doesn't see d0 at all.  gc clears c1 and c2.
0534     # Clearing c1 has the side effect of dropping the refcount on d0 to 0, so
0535     # d0 goes away (despite that it's in an older generation) and d0's __del__
0536     # triggers.  That in turn materializes a reference to c2 via c2wr(), but
0537     # c2 gets cleared anyway by gc.
0538 
0539     # We want to let gc happen "naturally", to preserve the distinction
0540     # between generations.
0541     detector = GC_Detector()
0542     junk = []
0543     i = 0
0544     while not detector.gc_happened:
0545         i += 1
0546         if i > 10000:
0547             raise TestFailed("gc didn't happen after 10000 iterations")
0548         expect(len(ouch), 0, "bug1055820d")
0549         junk.append([])  # this will eventually trigger gc
0550 
0551     expect(len(ouch), 1, "bug1055820d")  # else __del__ wasn't invoked
0552     for x in ouch:
0553         # If __del__ resurrected c2, the instance would be damaged, with an
0554         # empty __dict__.
0555         expect(x, None, "bug1055820d")
0556 
0557 
0558 def test_all():
0559     gc.collect() # Delete 2nd generation garbage
0560     run_test("lists", test_list)
0561     run_test("dicts", test_dict)
0562     run_test("tuples", test_tuple)
0563     run_test("classes", test_class)
0564     run_test("new style classes", test_newstyleclass)
0565     run_test("instances", test_instance)
0566     run_test("new instances", test_newinstance)
0567     run_test("methods", test_method)
0568     run_test("functions", test_function)
0569     run_test("frames", test_frame)
0570     run_test("finalizers", test_finalizer)
0571     run_test("finalizers (new class)", test_finalizer_newclass)
0572     run_test("__del__", test_del)
0573     run_test("__del__ (new class)", test_del_newclass)
0574     run_test("saveall", test_saveall)
0575     run_test("trashcan", test_trashcan)
0576     run_test("boom", test_boom)
0577     run_test("boom2", test_boom2)
0578     run_test("boom_new", test_boom_new)
0579     run_test("boom2_new", test_boom2_new)
0580     run_test("get_referents", test_get_referents)
0581     run_test("bug1055820b", test_bug1055820b)
0582 
0583     gc.enable()
0584     try:
0585         run_test("bug1055820c", test_bug1055820c)
0586     finally:
0587         gc.disable()
0588 
0589     gc.enable()
0590     try:
0591         run_test("bug1055820d", test_bug1055820d)
0592     finally:
0593         gc.disable()
0594 
0595 def test():
0596     if verbose:
0597         print "disabling automatic collection"
0598     enabled = gc.isenabled()
0599     gc.disable()
0600     verify(not gc.isenabled())
0601     debug = gc.get_debug()
0602     gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
0603 
0604     try:
0605         test_all()
0606     finally:
0607         gc.set_debug(debug)
0608         # test gc.enable() even if GC is disabled by default
0609         if verbose:
0610             print "restoring automatic collection"
0611         # make sure to always test gc.enable()
0612         gc.enable()
0613         verify(gc.isenabled())
0614         if not enabled:
0615             gc.disable()
0616 
0617 
0618 test()
0619 

Generated by PyXR 0.9.4
SourceForge.net Logo