blob: f60d5d9bd6ce56cd04797fa25298e30216bb0aff [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.
244 # For example:
245 # - disposed tuples are not freed, but reused
246 # - the call to assertEqual somehow avoids building its args tuple
Brett Cannon7a540732011-02-22 03:04:06 +0000247 @refcount_test
Guido van Rossumd8faa362007-04-27 19:54:29 +0000248 def test_get_count(self):
Christian Heimesa156e092008-02-16 07:38:31 +0000249 # Avoid future allocation of method object
Benjamin Peterson7fe73a12009-04-04 16:35:46 +0000250 assertEqual = self._baseAssertEqual
Guido van Rossumd8faa362007-04-27 19:54:29 +0000251 gc.collect()
Christian Heimesa156e092008-02-16 07:38:31 +0000252 assertEqual(gc.get_count(), (0, 0, 0))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000253 a = dict()
Christian Heimesa156e092008-02-16 07:38:31 +0000254 # since gc.collect(), we created two objects:
255 # the dict, and the tuple returned by get_count()
256 assertEqual(gc.get_count(), (2, 0, 0))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000257
Brett Cannon7a540732011-02-22 03:04:06 +0000258 @refcount_test
Guido van Rossumd8faa362007-04-27 19:54:29 +0000259 def test_collect_generations(self):
Christian Heimesa156e092008-02-16 07:38:31 +0000260 # Avoid future allocation of method object
261 assertEqual = self.assertEqual
Guido van Rossumd8faa362007-04-27 19:54:29 +0000262 gc.collect()
263 a = dict()
264 gc.collect(0)
Christian Heimesa156e092008-02-16 07:38:31 +0000265 assertEqual(gc.get_count(), (0, 1, 0))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000266 gc.collect(1)
Christian Heimesa156e092008-02-16 07:38:31 +0000267 assertEqual(gc.get_count(), (0, 0, 1))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000268 gc.collect(2)
Christian Heimesa156e092008-02-16 07:38:31 +0000269 assertEqual(gc.get_count(), (0, 0, 0))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000270
271 def test_trashcan(self):
272 class Ouch:
273 n = 0
274 def __del__(self):
275 Ouch.n = Ouch.n + 1
276 if Ouch.n % 17 == 0:
277 gc.collect()
278
279 # "trashcan" is a hack to prevent stack overflow when deallocating
280 # very deeply nested tuples etc. It works in part by abusing the
281 # type pointer and refcount fields, and that can yield horrible
282 # problems when gc tries to traverse the structures.
283 # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
284 # most likely die via segfault.
285
286 # Note: In 2.3 the possibility for compiling without cyclic gc was
287 # removed, and that in turn allows the trashcan mechanism to work
288 # via much simpler means (e.g., it never abuses the type pointer or
289 # refcount fields anymore). Since it's much less likely to cause a
290 # problem now, the various constants in this expensive (we force a lot
291 # of full collections) test are cut back from the 2.2 version.
292 gc.enable()
293 N = 150
294 for count in range(2):
295 t = []
296 for i in range(N):
297 t = [t, Ouch()]
298 u = []
299 for i in range(N):
300 u = [u, Ouch()]
301 v = {}
302 for i in range(N):
303 v = {1: v, 2: Ouch()}
304 gc.disable()
305
306 def test_boom(self):
307 class Boom:
308 def __getattr__(self, someattribute):
309 del self.attr
310 raise AttributeError
311
312 a = Boom()
313 b = Boom()
314 a.attr = b
315 b.attr = a
316
317 gc.collect()
318 garbagelen = len(gc.garbage)
319 del a, b
320 # a<->b are in a trash cycle now. Collection will invoke
321 # Boom.__getattr__ (to see whether a and b have __del__ methods), and
322 # __getattr__ deletes the internal "attr" attributes as a side effect.
323 # That causes the trash cycle to get reclaimed via refcounts falling to
324 # 0, thus mutating the trash graph as a side effect of merely asking
325 # whether __del__ exists. This used to (before 2.3b1) crash Python.
326 # Now __getattr__ isn't called.
327 self.assertEqual(gc.collect(), 4)
328 self.assertEqual(len(gc.garbage), garbagelen)
329
330 def test_boom2(self):
331 class Boom2:
332 def __init__(self):
333 self.x = 0
334
335 def __getattr__(self, someattribute):
336 self.x += 1
337 if self.x > 1:
338 del self.attr
339 raise AttributeError
340
341 a = Boom2()
342 b = Boom2()
343 a.attr = b
344 b.attr = a
345
346 gc.collect()
347 garbagelen = len(gc.garbage)
348 del a, b
349 # Much like test_boom(), except that __getattr__ doesn't break the
350 # cycle until the second time gc checks for __del__. As of 2.3b1,
351 # there isn't a second time, so this simply cleans up the trash cycle.
352 # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
353 # reclaimed this way.
354 self.assertEqual(gc.collect(), 4)
355 self.assertEqual(len(gc.garbage), garbagelen)
356
357 def test_boom_new(self):
358 # boom__new and boom2_new are exactly like boom and boom2, except use
359 # new-style classes.
360
361 class Boom_New(object):
362 def __getattr__(self, someattribute):
363 del self.attr
364 raise AttributeError
365
366 a = Boom_New()
367 b = Boom_New()
368 a.attr = b
369 b.attr = a
370
371 gc.collect()
372 garbagelen = len(gc.garbage)
373 del a, b
374 self.assertEqual(gc.collect(), 4)
375 self.assertEqual(len(gc.garbage), garbagelen)
376
377 def test_boom2_new(self):
378 class Boom2_New(object):
379 def __init__(self):
380 self.x = 0
381
382 def __getattr__(self, someattribute):
383 self.x += 1
384 if self.x > 1:
385 del self.attr
386 raise AttributeError
387
388 a = Boom2_New()
389 b = Boom2_New()
390 a.attr = b
391 b.attr = a
392
393 gc.collect()
394 garbagelen = len(gc.garbage)
395 del a, b
396 self.assertEqual(gc.collect(), 4)
397 self.assertEqual(len(gc.garbage), garbagelen)
398
399 def test_get_referents(self):
400 alist = [1, 3, 5]
401 got = gc.get_referents(alist)
402 got.sort()
403 self.assertEqual(got, alist)
404
405 atuple = tuple(alist)
406 got = gc.get_referents(atuple)
407 got.sort()
408 self.assertEqual(got, alist)
409
410 adict = {1: 3, 5: 7}
411 expected = [1, 3, 5, 7]
412 got = gc.get_referents(adict)
413 got.sort()
414 self.assertEqual(got, expected)
415
416 got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
417 got.sort()
Guido van Rossum805365e2007-05-07 22:24:25 +0000418 self.assertEqual(got, [0, 0] + list(range(5)))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000419
420 self.assertEqual(gc.get_referents(1, 'a', 4j), [])
421
Antoine Pitrou3a652b12009-03-23 18:52:06 +0000422 def test_is_tracked(self):
423 # Atomic built-in types are not tracked, user-defined objects and
424 # mutable containers are.
425 # NOTE: types with special optimizations (e.g. tuple) have tests
426 # in their own test files instead.
427 self.assertFalse(gc.is_tracked(None))
428 self.assertFalse(gc.is_tracked(1))
429 self.assertFalse(gc.is_tracked(1.0))
430 self.assertFalse(gc.is_tracked(1.0 + 5.0j))
431 self.assertFalse(gc.is_tracked(True))
432 self.assertFalse(gc.is_tracked(False))
433 self.assertFalse(gc.is_tracked(b"a"))
434 self.assertFalse(gc.is_tracked("a"))
435 self.assertFalse(gc.is_tracked(bytearray(b"a")))
436 self.assertFalse(gc.is_tracked(type))
437 self.assertFalse(gc.is_tracked(int))
438 self.assertFalse(gc.is_tracked(object))
439 self.assertFalse(gc.is_tracked(object()))
440
441 class UserClass:
442 pass
443 self.assertTrue(gc.is_tracked(gc))
444 self.assertTrue(gc.is_tracked(UserClass))
445 self.assertTrue(gc.is_tracked(UserClass()))
446 self.assertTrue(gc.is_tracked([]))
447 self.assertTrue(gc.is_tracked(set()))
448
Guido van Rossumd8faa362007-04-27 19:54:29 +0000449 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
Antoine Pitrou696e0352010-08-08 22:18:46 +0000473 def test_garbage_at_shutdown(self):
474 import subprocess
475 code = """if 1:
476 import gc
477 class X:
478 def __init__(self, name):
479 self.name = name
480 def __repr__(self):
481 return "<X %%r>" %% self.name
482 def __del__(self):
483 pass
484
485 x = X('first')
486 x.x = x
487 x.y = X('second')
488 del x
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000489 gc.set_debug(%s)
Antoine Pitrou696e0352010-08-08 22:18:46 +0000490 """
491 def run_command(code):
Georg Brandl08be72d2010-10-24 15:11:22 +0000492 p = subprocess.Popen([sys.executable, "-Wd", "-c", code],
Antoine Pitrou696e0352010-08-08 22:18:46 +0000493 stdout=subprocess.PIPE,
494 stderr=subprocess.PIPE)
495 stdout, stderr = p.communicate()
Brian Curtin8291af22010-11-01 16:40:17 +0000496 p.stdout.close()
497 p.stderr.close()
Antoine Pitrou696e0352010-08-08 22:18:46 +0000498 self.assertEqual(p.returncode, 0)
499 self.assertEqual(stdout.strip(), b"")
500 return strip_python_stderr(stderr)
501
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000502 stderr = run_command(code % "0")
Georg Brandl08be72d2010-10-24 15:11:22 +0000503 self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
504 b"shutdown; use", stderr)
Antoine Pitrouaee47562010-09-16 15:04:49 +0000505 self.assertNotIn(b"<X 'first'>", stderr)
Antoine Pitrou696e0352010-08-08 22:18:46 +0000506 # With DEBUG_UNCOLLECTABLE, the garbage list gets printed
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000507 stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE")
Georg Brandl08be72d2010-10-24 15:11:22 +0000508 self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
509 b"shutdown", stderr)
Antoine Pitrouaee47562010-09-16 15:04:49 +0000510 self.assertTrue(
511 (b"[<X 'first'>, <X 'second'>]" in stderr) or
512 (b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000513 # With DEBUG_SAVEALL, no additional message should get printed
514 # (because gc.garbage also contains normally reclaimable cyclic
515 # references, and its elements get printed at runtime anyway).
516 stderr = run_command(code % "gc.DEBUG_SAVEALL")
517 self.assertNotIn(b"uncollectable objects at shutdown", stderr)
518
Antoine Pitrou696e0352010-08-08 22:18:46 +0000519
Guido van Rossumd8faa362007-04-27 19:54:29 +0000520class GCTogglingTests(unittest.TestCase):
521 def setUp(self):
522 gc.enable()
523
524 def tearDown(self):
525 gc.disable()
526
527 def test_bug1055820c(self):
528 # Corresponds to temp2c.py in the bug report. This is pretty
529 # elaborate.
530
531 c0 = C1055820(0)
532 # Move c0 into generation 2.
533 gc.collect()
534
535 c1 = C1055820(1)
536 c1.keep_c0_alive = c0
537 del c0.loop # now only c1 keeps c0 alive
538
539 c2 = C1055820(2)
540 c2wr = weakref.ref(c2) # no callback!
541
542 ouch = []
543 def callback(ignored):
Tim Petersead8b7a2004-10-30 23:09:22 +0000544 ouch[:] = [c2wr()]
545
Guido van Rossumd8faa362007-04-27 19:54:29 +0000546 # The callback gets associated with a wr on an object in generation 2.
547 c0wr = weakref.ref(c0, callback)
Tim Petersead8b7a2004-10-30 23:09:22 +0000548
Guido van Rossumd8faa362007-04-27 19:54:29 +0000549 c0 = c1 = c2 = None
Tim Petersead8b7a2004-10-30 23:09:22 +0000550
Guido van Rossumd8faa362007-04-27 19:54:29 +0000551 # What we've set up: c0, c1, and c2 are all trash now. c0 is in
552 # generation 2. The only thing keeping it alive is that c1 points to
553 # it. c1 and c2 are in generation 0, and are in self-loops. There's a
554 # global weakref to c2 (c2wr), but that weakref has no callback.
555 # There's also a global weakref to c0 (c0wr), and that does have a
556 # callback, and that callback references c2 via c2wr().
557 #
558 # c0 has a wr with callback, which references c2wr
559 # ^
560 # |
561 # | Generation 2 above dots
562 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
563 # | Generation 0 below dots
564 # |
565 # |
566 # ^->c1 ^->c2 has a wr but no callback
567 # | | | |
568 # <--v <--v
569 #
570 # So this is the nightmare: when generation 0 gets collected, we see
571 # that c2 has a callback-free weakref, and c1 doesn't even have a
572 # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
573 # the only object that has a weakref with a callback. gc clears c1
574 # and c2. Clearing c1 has the side effect of dropping the refcount on
575 # c0 to 0, so c0 goes away (despite that it's in an older generation)
576 # and c0's wr callback triggers. That in turn materializes a reference
577 # to c2 via c2wr(), but c2 gets cleared anyway by gc.
Tim Petersead8b7a2004-10-30 23:09:22 +0000578
Guido van Rossumd8faa362007-04-27 19:54:29 +0000579 # We want to let gc happen "naturally", to preserve the distinction
580 # between generations.
581 junk = []
582 i = 0
583 detector = GC_Detector()
584 while not detector.gc_happened:
585 i += 1
586 if i > 10000:
587 self.fail("gc didn't happen after 10000 iterations")
588 self.assertEqual(len(ouch), 0)
589 junk.append([]) # this will eventually trigger gc
Tim Petersead8b7a2004-10-30 23:09:22 +0000590
Guido van Rossumd8faa362007-04-27 19:54:29 +0000591 self.assertEqual(len(ouch), 1) # else the callback wasn't invoked
592 for x in ouch:
593 # If the callback resurrected c2, the instance would be damaged,
594 # with an empty __dict__.
595 self.assertEqual(x, None)
Tim Petersead8b7a2004-10-30 23:09:22 +0000596
Guido van Rossumd8faa362007-04-27 19:54:29 +0000597 def test_bug1055820d(self):
598 # Corresponds to temp2d.py in the bug report. This is very much like
599 # test_bug1055820c, but uses a __del__ method instead of a weakref
600 # callback to sneak in a resurrection of cyclic trash.
Tim Petersead8b7a2004-10-30 23:09:22 +0000601
Guido van Rossumd8faa362007-04-27 19:54:29 +0000602 ouch = []
603 class D(C1055820):
604 def __del__(self):
605 ouch[:] = [c2wr()]
Tim Petersead8b7a2004-10-30 23:09:22 +0000606
Guido van Rossumd8faa362007-04-27 19:54:29 +0000607 d0 = D(0)
608 # Move all the above into generation 2.
609 gc.collect()
Tim Petersead8b7a2004-10-30 23:09:22 +0000610
Guido van Rossumd8faa362007-04-27 19:54:29 +0000611 c1 = C1055820(1)
612 c1.keep_d0_alive = d0
613 del d0.loop # now only c1 keeps d0 alive
Tim Petersead8b7a2004-10-30 23:09:22 +0000614
Guido van Rossumd8faa362007-04-27 19:54:29 +0000615 c2 = C1055820(2)
616 c2wr = weakref.ref(c2) # no callback!
Tim Petersead8b7a2004-10-30 23:09:22 +0000617
Guido van Rossumd8faa362007-04-27 19:54:29 +0000618 d0 = c1 = c2 = None
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000619
Guido van Rossumd8faa362007-04-27 19:54:29 +0000620 # What we've set up: d0, c1, and c2 are all trash now. d0 is in
621 # generation 2. The only thing keeping it alive is that c1 points to
622 # it. c1 and c2 are in generation 0, and are in self-loops. There's
623 # a global weakref to c2 (c2wr), but that weakref has no callback.
624 # There are no other weakrefs.
625 #
626 # d0 has a __del__ method that references c2wr
627 # ^
628 # |
629 # | Generation 2 above dots
630 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
631 # | Generation 0 below dots
632 # |
633 # |
634 # ^->c1 ^->c2 has a wr but no callback
635 # | | | |
636 # <--v <--v
637 #
638 # So this is the nightmare: when generation 0 gets collected, we see
639 # that c2 has a callback-free weakref, and c1 doesn't even have a
640 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
641 # c1 and c2. Clearing c1 has the side effect of dropping the refcount
642 # on d0 to 0, so d0 goes away (despite that it's in an older
643 # generation) and d0's __del__ triggers. That in turn materializes
644 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
645
646 # We want to let gc happen "naturally", to preserve the distinction
647 # between generations.
648 detector = GC_Detector()
649 junk = []
650 i = 0
651 while not detector.gc_happened:
652 i += 1
653 if i > 10000:
654 self.fail("gc didn't happen after 10000 iterations")
655 self.assertEqual(len(ouch), 0)
656 junk.append([]) # this will eventually trigger gc
657
658 self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked
659 for x in ouch:
660 # If __del__ resurrected c2, the instance would be damaged, with an
661 # empty __dict__.
662 self.assertEqual(x, None)
663
664def test_main():
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000665 enabled = gc.isenabled()
666 gc.disable()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000667 assert not gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000668 debug = gc.get_debug()
669 gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000670
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000671 try:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000672 gc.collect() # Delete 2nd generation garbage
673 run_unittest(GCTests, GCTogglingTests)
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000674 finally:
675 gc.set_debug(debug)
676 # test gc.enable() even if GC is disabled by default
677 if verbose:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000678 print("restoring automatic collection")
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000679 # make sure to always test gc.enable()
680 gc.enable()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000681 assert gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000682 if not enabled:
683 gc.disable()
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000684
Guido van Rossumd8faa362007-04-27 19:54:29 +0000685if __name__ == "__main__":
686 test_main()