blob: 19313db796a0c0622ba23363f765efd3a7849774 [file] [log] [blame]
Guido van Rossumd8faa362007-04-27 19:54:29 +00001import unittest
Brett Cannon7a540732011-02-22 03:04:06 +00002from test.support import (verbose, refcount_test, run_unittest,
3 strip_python_stderr)
Neil Schemenauer88c761a2001-07-12 13:25:53 +00004import sys
Jeremy Hyltonc5007aa2000-06-30 05:02:53 +00005import gc
Tim Petersead8b7a2004-10-30 23:09:22 +00006import weakref
Jeremy Hyltonc5007aa2000-06-30 05:02:53 +00007
Guido van Rossumd8faa362007-04-27 19:54:29 +00008### Support code
9###############################################################################
Tim Peters0f81ab62003-04-08 16:39:48 +000010
Tim Petersead8b7a2004-10-30 23:09:22 +000011# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
12# cyclic gc.
13
14# An instance of C1055820 has a self-loop, so becomes cyclic trash when
15# unreachable.
16class C1055820(object):
17 def __init__(self, i):
18 self.i = i
19 self.loop = self
20
21class GC_Detector(object):
22 # Create an instance I. Then gc hasn't happened again so long as
23 # I.gc_happened is false.
24
25 def __init__(self):
26 self.gc_happened = False
27
28 def it_happened(ignored):
29 self.gc_happened = True
30
31 # Create a piece of cyclic trash that triggers it_happened when
32 # gc collects it.
33 self.wr = weakref.ref(C1055820(666), it_happened)
34
Tim Petersead8b7a2004-10-30 23:09:22 +000035
Guido van Rossumd8faa362007-04-27 19:54:29 +000036### Tests
37###############################################################################
Tim Petersead8b7a2004-10-30 23:09:22 +000038
Guido van Rossumd8faa362007-04-27 19:54:29 +000039class GCTests(unittest.TestCase):
40 def test_list(self):
41 l = []
42 l.append(l)
43 gc.collect()
44 del l
45 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +000046
Guido van Rossumd8faa362007-04-27 19:54:29 +000047 def test_dict(self):
48 d = {}
49 d[1] = d
50 gc.collect()
51 del d
52 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +000053
Guido van Rossumd8faa362007-04-27 19:54:29 +000054 def test_tuple(self):
55 # since tuples are immutable we close the loop with a list
56 l = []
57 t = (l,)
58 l.append(t)
59 gc.collect()
60 del t
61 del l
62 self.assertEqual(gc.collect(), 2)
Tim Petersead8b7a2004-10-30 23:09:22 +000063
Guido van Rossumd8faa362007-04-27 19:54:29 +000064 def test_class(self):
65 class A:
66 pass
67 A.a = A
68 gc.collect()
69 del A
70 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000071
Guido van Rossumd8faa362007-04-27 19:54:29 +000072 def test_newstyleclass(self):
73 class A(object):
74 pass
75 gc.collect()
76 del A
77 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000078
Guido van Rossumd8faa362007-04-27 19:54:29 +000079 def test_instance(self):
80 class A:
81 pass
82 a = A()
83 a.a = a
84 gc.collect()
85 del a
86 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000087
Guido van Rossumd8faa362007-04-27 19:54:29 +000088 def test_newinstance(self):
89 class A(object):
90 pass
91 a = A()
92 a.a = a
93 gc.collect()
94 del a
95 self.assertNotEqual(gc.collect(), 0)
96 class B(list):
97 pass
98 class C(B, A):
99 pass
100 a = C()
101 a.a = a
102 gc.collect()
103 del a
104 self.assertNotEqual(gc.collect(), 0)
105 del B, C
106 self.assertNotEqual(gc.collect(), 0)
107 A.a = A()
108 del A
109 self.assertNotEqual(gc.collect(), 0)
110 self.assertEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +0000111
Guido van Rossumd8faa362007-04-27 19:54:29 +0000112 def test_method(self):
113 # Tricky: self.__init__ is a bound method, it references the instance.
114 class A:
115 def __init__(self):
116 self.init = self.__init__
117 a = A()
118 gc.collect()
119 del a
120 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +0000121
Guido van Rossumd8faa362007-04-27 19:54:29 +0000122 def test_finalizer(self):
123 # A() is uncollectable if it is part of a cycle, make sure it shows up
124 # in gc.garbage.
125 class A:
126 def __del__(self): pass
127 class B:
128 pass
129 a = A()
130 a.a = a
131 id_a = id(a)
132 b = B()
133 b.b = b
134 gc.collect()
135 del a
136 del b
137 self.assertNotEqual(gc.collect(), 0)
138 for obj in gc.garbage:
139 if id(obj) == id_a:
140 del obj.a
141 break
142 else:
143 self.fail("didn't find obj in garbage (finalizer)")
144 gc.garbage.remove(obj)
Tim Petersead8b7a2004-10-30 23:09:22 +0000145
Guido van Rossumd8faa362007-04-27 19:54:29 +0000146 def test_finalizer_newclass(self):
147 # A() is uncollectable if it is part of a cycle, make sure it shows up
148 # in gc.garbage.
149 class A(object):
150 def __del__(self): pass
151 class B(object):
152 pass
153 a = A()
154 a.a = a
155 id_a = id(a)
156 b = B()
157 b.b = b
158 gc.collect()
159 del a
160 del b
161 self.assertNotEqual(gc.collect(), 0)
162 for obj in gc.garbage:
163 if id(obj) == id_a:
164 del obj.a
165 break
166 else:
167 self.fail("didn't find obj in garbage (finalizer)")
168 gc.garbage.remove(obj)
Tim Petersead8b7a2004-10-30 23:09:22 +0000169
Guido van Rossumd8faa362007-04-27 19:54:29 +0000170 def test_function(self):
171 # Tricky: f -> d -> f, code should call d.clear() after the exec to
172 # break the cycle.
173 d = {}
174 exec("def f(): pass\n", d)
175 gc.collect()
176 del d
177 self.assertEqual(gc.collect(), 2)
Tim Petersead8b7a2004-10-30 23:09:22 +0000178
Brett Cannon7a540732011-02-22 03:04:06 +0000179 @refcount_test
Guido van Rossumd8faa362007-04-27 19:54:29 +0000180 def test_frame(self):
181 def f():
182 frame = sys._getframe()
183 gc.collect()
184 f()
185 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +0000186
Guido van Rossumd8faa362007-04-27 19:54:29 +0000187 def test_saveall(self):
188 # Verify that cyclic garbage like lists show up in gc.garbage if the
189 # SAVEALL option is enabled.
Tim Petersead8b7a2004-10-30 23:09:22 +0000190
Guido van Rossumd8faa362007-04-27 19:54:29 +0000191 # First make sure we don't save away other stuff that just happens to
192 # be waiting for collection.
193 gc.collect()
194 # if this fails, someone else created immortal trash
195 self.assertEqual(gc.garbage, [])
196
197 L = []
198 L.append(L)
199 id_L = id(L)
200
201 debug = gc.get_debug()
202 gc.set_debug(debug | gc.DEBUG_SAVEALL)
203 del L
204 gc.collect()
205 gc.set_debug(debug)
206
207 self.assertEqual(len(gc.garbage), 1)
208 obj = gc.garbage.pop()
209 self.assertEqual(id(obj), id_L)
210
211 def test_del(self):
212 # __del__ methods can trigger collection, make this to happen
213 thresholds = gc.get_threshold()
214 gc.enable()
215 gc.set_threshold(1)
216
217 class A:
218 def __del__(self):
219 dir(self)
220 a = A()
221 del a
222
223 gc.disable()
224 gc.set_threshold(*thresholds)
225
226 def test_del_newclass(self):
227 # __del__ methods can trigger collection, make this to happen
228 thresholds = gc.get_threshold()
229 gc.enable()
230 gc.set_threshold(1)
231
232 class A(object):
233 def __del__(self):
234 dir(self)
235 a = A()
236 del a
237
238 gc.disable()
239 gc.set_threshold(*thresholds)
240
Christian Heimesa156e092008-02-16 07:38:31 +0000241 # The following two tests are fragile:
242 # They precisely count the number of allocations,
243 # which is highly implementation-dependent.
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200244 # For example, disposed tuples are not freed, but reused.
245 # To minimize variations, though, we first store the get_count() results
246 # and check them at the end.
Brett Cannon7a540732011-02-22 03:04:06 +0000247 @refcount_test
Guido van Rossumd8faa362007-04-27 19:54:29 +0000248 def test_get_count(self):
249 gc.collect()
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200250 a, b, c = gc.get_count()
251 x = []
252 d, e, f = gc.get_count()
253 self.assertEqual((b, c), (0, 0))
254 self.assertEqual((e, f), (0, 0))
255 # This is less fragile than asserting that a equals 0.
256 self.assertLess(a, 5)
257 # Between the two calls to get_count(), at least one object was
258 # created (the list).
259 self.assertGreater(d, a)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000260
Brett Cannon7a540732011-02-22 03:04:06 +0000261 @refcount_test
Guido van Rossumd8faa362007-04-27 19:54:29 +0000262 def test_collect_generations(self):
263 gc.collect()
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200264 # This object will "trickle" into generation N + 1 after
265 # each call to collect(N)
266 x = []
Guido van Rossumd8faa362007-04-27 19:54:29 +0000267 gc.collect(0)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200268 # x is now in gen 1
269 a, b, c = gc.get_count()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000270 gc.collect(1)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200271 # x is now in gen 2
272 d, e, f = gc.get_count()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000273 gc.collect(2)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200274 # x is now in gen 3
275 g, h, i = gc.get_count()
276 # We don't check a, d, g since their exact values depends on
277 # internal implementation details of the interpreter.
278 self.assertEqual((b, c), (1, 0))
279 self.assertEqual((e, f), (0, 1))
280 self.assertEqual((h, i), (0, 0))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000281
282 def test_trashcan(self):
283 class Ouch:
284 n = 0
285 def __del__(self):
286 Ouch.n = Ouch.n + 1
287 if Ouch.n % 17 == 0:
288 gc.collect()
289
290 # "trashcan" is a hack to prevent stack overflow when deallocating
291 # very deeply nested tuples etc. It works in part by abusing the
292 # type pointer and refcount fields, and that can yield horrible
293 # problems when gc tries to traverse the structures.
294 # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
295 # most likely die via segfault.
296
297 # Note: In 2.3 the possibility for compiling without cyclic gc was
298 # removed, and that in turn allows the trashcan mechanism to work
299 # via much simpler means (e.g., it never abuses the type pointer or
300 # refcount fields anymore). Since it's much less likely to cause a
301 # problem now, the various constants in this expensive (we force a lot
302 # of full collections) test are cut back from the 2.2 version.
303 gc.enable()
304 N = 150
305 for count in range(2):
306 t = []
307 for i in range(N):
308 t = [t, Ouch()]
309 u = []
310 for i in range(N):
311 u = [u, Ouch()]
312 v = {}
313 for i in range(N):
314 v = {1: v, 2: Ouch()}
315 gc.disable()
316
317 def test_boom(self):
318 class Boom:
319 def __getattr__(self, someattribute):
320 del self.attr
321 raise AttributeError
322
323 a = Boom()
324 b = Boom()
325 a.attr = b
326 b.attr = a
327
328 gc.collect()
329 garbagelen = len(gc.garbage)
330 del a, b
331 # a<->b are in a trash cycle now. Collection will invoke
332 # Boom.__getattr__ (to see whether a and b have __del__ methods), and
333 # __getattr__ deletes the internal "attr" attributes as a side effect.
334 # That causes the trash cycle to get reclaimed via refcounts falling to
335 # 0, thus mutating the trash graph as a side effect of merely asking
336 # whether __del__ exists. This used to (before 2.3b1) crash Python.
337 # Now __getattr__ isn't called.
338 self.assertEqual(gc.collect(), 4)
339 self.assertEqual(len(gc.garbage), garbagelen)
340
341 def test_boom2(self):
342 class Boom2:
343 def __init__(self):
344 self.x = 0
345
346 def __getattr__(self, someattribute):
347 self.x += 1
348 if self.x > 1:
349 del self.attr
350 raise AttributeError
351
352 a = Boom2()
353 b = Boom2()
354 a.attr = b
355 b.attr = a
356
357 gc.collect()
358 garbagelen = len(gc.garbage)
359 del a, b
360 # Much like test_boom(), except that __getattr__ doesn't break the
361 # cycle until the second time gc checks for __del__. As of 2.3b1,
362 # there isn't a second time, so this simply cleans up the trash cycle.
363 # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
364 # reclaimed this way.
365 self.assertEqual(gc.collect(), 4)
366 self.assertEqual(len(gc.garbage), garbagelen)
367
368 def test_boom_new(self):
369 # boom__new and boom2_new are exactly like boom and boom2, except use
370 # new-style classes.
371
372 class Boom_New(object):
373 def __getattr__(self, someattribute):
374 del self.attr
375 raise AttributeError
376
377 a = Boom_New()
378 b = Boom_New()
379 a.attr = b
380 b.attr = a
381
382 gc.collect()
383 garbagelen = len(gc.garbage)
384 del a, b
385 self.assertEqual(gc.collect(), 4)
386 self.assertEqual(len(gc.garbage), garbagelen)
387
388 def test_boom2_new(self):
389 class Boom2_New(object):
390 def __init__(self):
391 self.x = 0
392
393 def __getattr__(self, someattribute):
394 self.x += 1
395 if self.x > 1:
396 del self.attr
397 raise AttributeError
398
399 a = Boom2_New()
400 b = Boom2_New()
401 a.attr = b
402 b.attr = a
403
404 gc.collect()
405 garbagelen = len(gc.garbage)
406 del a, b
407 self.assertEqual(gc.collect(), 4)
408 self.assertEqual(len(gc.garbage), garbagelen)
409
410 def test_get_referents(self):
411 alist = [1, 3, 5]
412 got = gc.get_referents(alist)
413 got.sort()
414 self.assertEqual(got, alist)
415
416 atuple = tuple(alist)
417 got = gc.get_referents(atuple)
418 got.sort()
419 self.assertEqual(got, alist)
420
421 adict = {1: 3, 5: 7}
422 expected = [1, 3, 5, 7]
423 got = gc.get_referents(adict)
424 got.sort()
425 self.assertEqual(got, expected)
426
427 got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
428 got.sort()
Guido van Rossum805365e2007-05-07 22:24:25 +0000429 self.assertEqual(got, [0, 0] + list(range(5)))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000430
431 self.assertEqual(gc.get_referents(1, 'a', 4j), [])
432
Antoine Pitrou3a652b12009-03-23 18:52:06 +0000433 def test_is_tracked(self):
434 # Atomic built-in types are not tracked, user-defined objects and
435 # mutable containers are.
436 # NOTE: types with special optimizations (e.g. tuple) have tests
437 # in their own test files instead.
438 self.assertFalse(gc.is_tracked(None))
439 self.assertFalse(gc.is_tracked(1))
440 self.assertFalse(gc.is_tracked(1.0))
441 self.assertFalse(gc.is_tracked(1.0 + 5.0j))
442 self.assertFalse(gc.is_tracked(True))
443 self.assertFalse(gc.is_tracked(False))
444 self.assertFalse(gc.is_tracked(b"a"))
445 self.assertFalse(gc.is_tracked("a"))
446 self.assertFalse(gc.is_tracked(bytearray(b"a")))
447 self.assertFalse(gc.is_tracked(type))
448 self.assertFalse(gc.is_tracked(int))
449 self.assertFalse(gc.is_tracked(object))
450 self.assertFalse(gc.is_tracked(object()))
451
452 class UserClass:
453 pass
454 self.assertTrue(gc.is_tracked(gc))
455 self.assertTrue(gc.is_tracked(UserClass))
456 self.assertTrue(gc.is_tracked(UserClass()))
457 self.assertTrue(gc.is_tracked([]))
458 self.assertTrue(gc.is_tracked(set()))
459
Guido van Rossumd8faa362007-04-27 19:54:29 +0000460 def test_bug1055820b(self):
461 # Corresponds to temp2b.py in the bug report.
462
463 ouch = []
464 def callback(ignored):
465 ouch[:] = [wr() for wr in WRs]
466
467 Cs = [C1055820(i) for i in range(2)]
468 WRs = [weakref.ref(c, callback) for c in Cs]
469 c = None
470
471 gc.collect()
472 self.assertEqual(len(ouch), 0)
473 # Make the two instances trash, and collect again. The bug was that
474 # the callback materialized a strong reference to an instance, but gc
475 # cleared the instance's dict anyway.
476 Cs = None
477 gc.collect()
478 self.assertEqual(len(ouch), 2) # else the callbacks didn't run
479 for x in ouch:
480 # If the callback resurrected one of these guys, the instance
481 # would be damaged, with an empty __dict__.
482 self.assertEqual(x, None)
483
Antoine Pitrou696e0352010-08-08 22:18:46 +0000484 def test_garbage_at_shutdown(self):
485 import subprocess
486 code = """if 1:
487 import gc
488 class X:
489 def __init__(self, name):
490 self.name = name
491 def __repr__(self):
492 return "<X %%r>" %% self.name
493 def __del__(self):
494 pass
495
496 x = X('first')
497 x.x = x
498 x.y = X('second')
499 del x
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000500 gc.set_debug(%s)
Antoine Pitrou696e0352010-08-08 22:18:46 +0000501 """
502 def run_command(code):
Georg Brandl08be72d2010-10-24 15:11:22 +0000503 p = subprocess.Popen([sys.executable, "-Wd", "-c", code],
Antoine Pitrou696e0352010-08-08 22:18:46 +0000504 stdout=subprocess.PIPE,
505 stderr=subprocess.PIPE)
506 stdout, stderr = p.communicate()
Brian Curtin8291af22010-11-01 16:40:17 +0000507 p.stdout.close()
508 p.stderr.close()
Antoine Pitrou696e0352010-08-08 22:18:46 +0000509 self.assertEqual(p.returncode, 0)
510 self.assertEqual(stdout.strip(), b"")
511 return strip_python_stderr(stderr)
512
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000513 stderr = run_command(code % "0")
Georg Brandl08be72d2010-10-24 15:11:22 +0000514 self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
515 b"shutdown; use", stderr)
Antoine Pitrouaee47562010-09-16 15:04:49 +0000516 self.assertNotIn(b"<X 'first'>", stderr)
Antoine Pitrou696e0352010-08-08 22:18:46 +0000517 # With DEBUG_UNCOLLECTABLE, the garbage list gets printed
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000518 stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE")
Georg Brandl08be72d2010-10-24 15:11:22 +0000519 self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
520 b"shutdown", stderr)
Antoine Pitrouaee47562010-09-16 15:04:49 +0000521 self.assertTrue(
522 (b"[<X 'first'>, <X 'second'>]" in stderr) or
523 (b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000524 # With DEBUG_SAVEALL, no additional message should get printed
525 # (because gc.garbage also contains normally reclaimable cyclic
526 # references, and its elements get printed at runtime anyway).
527 stderr = run_command(code % "gc.DEBUG_SAVEALL")
528 self.assertNotIn(b"uncollectable objects at shutdown", stderr)
529
Antoine Pitrou696e0352010-08-08 22:18:46 +0000530
Guido van Rossumd8faa362007-04-27 19:54:29 +0000531class GCTogglingTests(unittest.TestCase):
532 def setUp(self):
533 gc.enable()
534
535 def tearDown(self):
536 gc.disable()
537
538 def test_bug1055820c(self):
539 # Corresponds to temp2c.py in the bug report. This is pretty
540 # elaborate.
541
542 c0 = C1055820(0)
543 # Move c0 into generation 2.
544 gc.collect()
545
546 c1 = C1055820(1)
547 c1.keep_c0_alive = c0
548 del c0.loop # now only c1 keeps c0 alive
549
550 c2 = C1055820(2)
551 c2wr = weakref.ref(c2) # no callback!
552
553 ouch = []
554 def callback(ignored):
Tim Petersead8b7a2004-10-30 23:09:22 +0000555 ouch[:] = [c2wr()]
556
Guido van Rossumd8faa362007-04-27 19:54:29 +0000557 # The callback gets associated with a wr on an object in generation 2.
558 c0wr = weakref.ref(c0, callback)
Tim Petersead8b7a2004-10-30 23:09:22 +0000559
Guido van Rossumd8faa362007-04-27 19:54:29 +0000560 c0 = c1 = c2 = None
Tim Petersead8b7a2004-10-30 23:09:22 +0000561
Guido van Rossumd8faa362007-04-27 19:54:29 +0000562 # What we've set up: c0, c1, and c2 are all trash now. c0 is in
563 # generation 2. The only thing keeping it alive is that c1 points to
564 # it. c1 and c2 are in generation 0, and are in self-loops. There's a
565 # global weakref to c2 (c2wr), but that weakref has no callback.
566 # There's also a global weakref to c0 (c0wr), and that does have a
567 # callback, and that callback references c2 via c2wr().
568 #
569 # c0 has a wr with callback, which references c2wr
570 # ^
571 # |
572 # | Generation 2 above dots
573 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
574 # | Generation 0 below dots
575 # |
576 # |
577 # ^->c1 ^->c2 has a wr but no callback
578 # | | | |
579 # <--v <--v
580 #
581 # So this is the nightmare: when generation 0 gets collected, we see
582 # that c2 has a callback-free weakref, and c1 doesn't even have a
583 # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
584 # the only object that has a weakref with a callback. gc clears c1
585 # and c2. Clearing c1 has the side effect of dropping the refcount on
586 # c0 to 0, so c0 goes away (despite that it's in an older generation)
587 # and c0's wr callback triggers. That in turn materializes a reference
588 # to c2 via c2wr(), but c2 gets cleared anyway by gc.
Tim Petersead8b7a2004-10-30 23:09:22 +0000589
Guido van Rossumd8faa362007-04-27 19:54:29 +0000590 # We want to let gc happen "naturally", to preserve the distinction
591 # between generations.
592 junk = []
593 i = 0
594 detector = GC_Detector()
595 while not detector.gc_happened:
596 i += 1
597 if i > 10000:
598 self.fail("gc didn't happen after 10000 iterations")
599 self.assertEqual(len(ouch), 0)
600 junk.append([]) # this will eventually trigger gc
Tim Petersead8b7a2004-10-30 23:09:22 +0000601
Guido van Rossumd8faa362007-04-27 19:54:29 +0000602 self.assertEqual(len(ouch), 1) # else the callback wasn't invoked
603 for x in ouch:
604 # If the callback resurrected c2, the instance would be damaged,
605 # with an empty __dict__.
606 self.assertEqual(x, None)
Tim Petersead8b7a2004-10-30 23:09:22 +0000607
Guido van Rossumd8faa362007-04-27 19:54:29 +0000608 def test_bug1055820d(self):
609 # Corresponds to temp2d.py in the bug report. This is very much like
610 # test_bug1055820c, but uses a __del__ method instead of a weakref
611 # callback to sneak in a resurrection of cyclic trash.
Tim Petersead8b7a2004-10-30 23:09:22 +0000612
Guido van Rossumd8faa362007-04-27 19:54:29 +0000613 ouch = []
614 class D(C1055820):
615 def __del__(self):
616 ouch[:] = [c2wr()]
Tim Petersead8b7a2004-10-30 23:09:22 +0000617
Guido van Rossumd8faa362007-04-27 19:54:29 +0000618 d0 = D(0)
619 # Move all the above into generation 2.
620 gc.collect()
Tim Petersead8b7a2004-10-30 23:09:22 +0000621
Guido van Rossumd8faa362007-04-27 19:54:29 +0000622 c1 = C1055820(1)
623 c1.keep_d0_alive = d0
624 del d0.loop # now only c1 keeps d0 alive
Tim Petersead8b7a2004-10-30 23:09:22 +0000625
Guido van Rossumd8faa362007-04-27 19:54:29 +0000626 c2 = C1055820(2)
627 c2wr = weakref.ref(c2) # no callback!
Tim Petersead8b7a2004-10-30 23:09:22 +0000628
Guido van Rossumd8faa362007-04-27 19:54:29 +0000629 d0 = c1 = c2 = None
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000630
Guido van Rossumd8faa362007-04-27 19:54:29 +0000631 # What we've set up: d0, c1, and c2 are all trash now. d0 is in
632 # generation 2. The only thing keeping it alive is that c1 points to
633 # it. c1 and c2 are in generation 0, and are in self-loops. There's
634 # a global weakref to c2 (c2wr), but that weakref has no callback.
635 # There are no other weakrefs.
636 #
637 # d0 has a __del__ method that references c2wr
638 # ^
639 # |
640 # | Generation 2 above dots
641 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
642 # | Generation 0 below dots
643 # |
644 # |
645 # ^->c1 ^->c2 has a wr but no callback
646 # | | | |
647 # <--v <--v
648 #
649 # So this is the nightmare: when generation 0 gets collected, we see
650 # that c2 has a callback-free weakref, and c1 doesn't even have a
651 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
652 # c1 and c2. Clearing c1 has the side effect of dropping the refcount
653 # on d0 to 0, so d0 goes away (despite that it's in an older
654 # generation) and d0's __del__ triggers. That in turn materializes
655 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
656
657 # We want to let gc happen "naturally", to preserve the distinction
658 # between generations.
659 detector = GC_Detector()
660 junk = []
661 i = 0
662 while not detector.gc_happened:
663 i += 1
664 if i > 10000:
665 self.fail("gc didn't happen after 10000 iterations")
666 self.assertEqual(len(ouch), 0)
667 junk.append([]) # this will eventually trigger gc
668
669 self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked
670 for x in ouch:
671 # If __del__ resurrected c2, the instance would be damaged, with an
672 # empty __dict__.
673 self.assertEqual(x, None)
674
675def test_main():
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000676 enabled = gc.isenabled()
677 gc.disable()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000678 assert not gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000679 debug = gc.get_debug()
680 gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000681
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000682 try:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000683 gc.collect() # Delete 2nd generation garbage
684 run_unittest(GCTests, GCTogglingTests)
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000685 finally:
686 gc.set_debug(debug)
687 # test gc.enable() even if GC is disabled by default
688 if verbose:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000689 print("restoring automatic collection")
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000690 # make sure to always test gc.enable()
691 gc.enable()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000692 assert gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000693 if not enabled:
694 gc.disable()
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000695
Guido van Rossumd8faa362007-04-27 19:54:29 +0000696if __name__ == "__main__":
697 test_main()