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