2 from test
.test_support
import verbose
, run_unittest
8 ###############################################################################
10 # Bug 1055820 has several tests of longstanding bugs involving weakrefs and
13 # An instance of C1055820 has a self-loop, so becomes cyclic trash when
15 class C1055820(object):
16 def __init__(self
, i
):
20 class GC_Detector(object):
21 # Create an instance I. Then gc hasn't happened again so long as
22 # I.gc_happened is false.
25 self
.gc_happened
= False
27 def it_happened(ignored
):
28 self
.gc_happened
= True
30 # Create a piece of cyclic trash that triggers it_happened when
32 self
.wr
= weakref
.ref(C1055820(666), it_happened
)
36 ###############################################################################
38 class GCTests(unittest
.TestCase
):
44 self
.assertEqual(gc
.collect(), 1)
51 self
.assertEqual(gc
.collect(), 1)
54 # since tuples are immutable we close the loop with a list
61 self
.assertEqual(gc
.collect(), 2)
69 self
.assertNotEqual(gc
.collect(), 0)
71 def test_newstyleclass(self
):
76 self
.assertNotEqual(gc
.collect(), 0)
78 def test_instance(self
):
85 self
.assertNotEqual(gc
.collect(), 0)
87 def test_newinstance(self
):
94 self
.assertNotEqual(gc
.collect(), 0)
103 self
.assertNotEqual(gc
.collect(), 0)
105 self
.assertNotEqual(gc
.collect(), 0)
108 self
.assertNotEqual(gc
.collect(), 0)
109 self
.assertEqual(gc
.collect(), 0)
111 def test_method(self
):
112 # Tricky: self.__init__ is a bound method, it references the instance.
115 self
.init
= self
.__init
__
119 self
.assertNotEqual(gc
.collect(), 0)
121 def test_finalizer(self
):
122 # A() is uncollectable if it is part of a cycle, make sure it shows up
125 def __del__(self
): pass
136 self
.assertNotEqual(gc
.collect(), 0)
137 for obj
in gc
.garbage
:
142 self
.fail("didn't find obj in garbage (finalizer)")
143 gc
.garbage
.remove(obj
)
145 def test_finalizer_newclass(self
):
146 # A() is uncollectable if it is part of a cycle, make sure it shows up
149 def __del__(self
): pass
160 self
.assertNotEqual(gc
.collect(), 0)
161 for obj
in gc
.garbage
:
166 self
.fail("didn't find obj in garbage (finalizer)")
167 gc
.garbage
.remove(obj
)
169 def test_function(self
):
170 # Tricky: f -> d -> f, code should call d.clear() after the exec to
173 exec("def f(): pass\n") in d
176 self
.assertEqual(gc
.collect(), 2)
178 def test_frame(self
):
180 frame
= sys
._getframe
()
183 self
.assertEqual(gc
.collect(), 1)
185 def test_saveall(self
):
186 # Verify that cyclic garbage like lists show up in gc.garbage if the
187 # SAVEALL option is enabled.
189 # First make sure we don't save away other stuff that just happens to
190 # be waiting for collection.
192 # if this fails, someone else created immortal trash
193 self
.assertEqual(gc
.garbage
, [])
199 debug
= gc
.get_debug()
200 gc
.set_debug(debug | gc
.DEBUG_SAVEALL
)
205 self
.assertEqual(len(gc
.garbage
), 1)
206 obj
= gc
.garbage
.pop()
207 self
.assertEqual(id(obj
), id_L
)
210 # __del__ methods can trigger collection, make this to happen
211 thresholds
= gc
.get_threshold()
222 gc
.set_threshold(*thresholds
)
224 def test_del_newclass(self
):
225 # __del__ methods can trigger collection, make this to happen
226 thresholds
= gc
.get_threshold()
237 gc
.set_threshold(*thresholds
)
239 # The following two tests are fragile:
240 # They precisely count the number of allocations,
241 # which is highly implementation-dependent.
243 # - disposed tuples are not freed, but reused
244 # - the call to assertEqual somehow avoids building its args tuple
245 def test_get_count(self
):
246 # Avoid future allocation of method object
247 assertEqual
= self
._baseAssertEqual
249 assertEqual(gc
.get_count(), (0, 0, 0))
251 # since gc.collect(), we created two objects:
252 # the dict, and the tuple returned by get_count()
253 assertEqual(gc
.get_count(), (2, 0, 0))
255 def test_collect_generations(self
):
256 # Avoid future allocation of method object
257 assertEqual
= self
.assertEqual
261 assertEqual(gc
.get_count(), (0, 1, 0))
263 assertEqual(gc
.get_count(), (0, 0, 1))
265 assertEqual(gc
.get_count(), (0, 0, 0))
267 def test_trashcan(self
):
275 # "trashcan" is a hack to prevent stack overflow when deallocating
276 # very deeply nested tuples etc. It works in part by abusing the
277 # type pointer and refcount fields, and that can yield horrible
278 # problems when gc tries to traverse the structures.
279 # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
280 # most likely die via segfault.
282 # Note: In 2.3 the possibility for compiling without cyclic gc was
283 # removed, and that in turn allows the trashcan mechanism to work
284 # via much simpler means (e.g., it never abuses the type pointer or
285 # refcount fields anymore). Since it's much less likely to cause a
286 # problem now, the various constants in this expensive (we force a lot
287 # of full collections) test are cut back from the 2.2 version.
290 for count
in range(2):
299 v
= {1: v
, 2: Ouch()}
304 def __getattr__(self
, someattribute
):
314 garbagelen
= len(gc
.garbage
)
316 # a<->b are in a trash cycle now. Collection will invoke
317 # Boom.__getattr__ (to see whether a and b have __del__ methods), and
318 # __getattr__ deletes the internal "attr" attributes as a side effect.
319 # That causes the trash cycle to get reclaimed via refcounts falling to
320 # 0, thus mutating the trash graph as a side effect of merely asking
321 # whether __del__ exists. This used to (before 2.3b1) crash Python.
322 # Now __getattr__ isn't called.
323 self
.assertEqual(gc
.collect(), 4)
324 self
.assertEqual(len(gc
.garbage
), garbagelen
)
326 def test_boom2(self
):
331 def __getattr__(self
, someattribute
):
343 garbagelen
= len(gc
.garbage
)
345 # Much like test_boom(), except that __getattr__ doesn't break the
346 # cycle until the second time gc checks for __del__. As of 2.3b1,
347 # there isn't a second time, so this simply cleans up the trash cycle.
348 # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
349 # reclaimed this way.
350 self
.assertEqual(gc
.collect(), 4)
351 self
.assertEqual(len(gc
.garbage
), garbagelen
)
353 def test_boom_new(self
):
354 # boom__new and boom2_new are exactly like boom and boom2, except use
357 class Boom_New(object):
358 def __getattr__(self
, someattribute
):
368 garbagelen
= len(gc
.garbage
)
370 self
.assertEqual(gc
.collect(), 4)
371 self
.assertEqual(len(gc
.garbage
), garbagelen
)
373 def test_boom2_new(self
):
374 class Boom2_New(object):
378 def __getattr__(self
, someattribute
):
390 garbagelen
= len(gc
.garbage
)
392 self
.assertEqual(gc
.collect(), 4)
393 self
.assertEqual(len(gc
.garbage
), garbagelen
)
395 def test_get_referents(self
):
397 got
= gc
.get_referents(alist
)
399 self
.assertEqual(got
, alist
)
401 atuple
= tuple(alist
)
402 got
= gc
.get_referents(atuple
)
404 self
.assertEqual(got
, alist
)
407 expected
= [1, 3, 5, 7]
408 got
= gc
.get_referents(adict
)
410 self
.assertEqual(got
, expected
)
412 got
= gc
.get_referents([1, 2], {3: 4}, (0, 0, 0))
414 self
.assertEqual(got
, [0, 0] + range(5))
416 self
.assertEqual(gc
.get_referents(1, 'a', 4j
), [])
418 def test_is_tracked(self
):
419 # Atomic built-in types are not tracked, user-defined objects and
420 # mutable containers are.
421 # NOTE: types with special optimizations (e.g. tuple) have tests
422 # in their own test files instead.
423 self
.assertFalse(gc
.is_tracked(None))
424 self
.assertFalse(gc
.is_tracked(1))
425 self
.assertFalse(gc
.is_tracked(1.0))
426 self
.assertFalse(gc
.is_tracked(1.0 + 5.0j
))
427 self
.assertFalse(gc
.is_tracked(True))
428 self
.assertFalse(gc
.is_tracked(False))
429 self
.assertFalse(gc
.is_tracked("a"))
430 self
.assertFalse(gc
.is_tracked(u
"a"))
431 self
.assertFalse(gc
.is_tracked(bytearray("a")))
432 self
.assertFalse(gc
.is_tracked(type))
433 self
.assertFalse(gc
.is_tracked(int))
434 self
.assertFalse(gc
.is_tracked(object))
435 self
.assertFalse(gc
.is_tracked(object()))
439 class NewStyle(object):
441 self
.assertTrue(gc
.is_tracked(gc
))
442 self
.assertTrue(gc
.is_tracked(OldStyle
))
443 self
.assertTrue(gc
.is_tracked(OldStyle()))
444 self
.assertTrue(gc
.is_tracked(NewStyle
))
445 self
.assertTrue(gc
.is_tracked(NewStyle()))
446 self
.assertTrue(gc
.is_tracked([]))
447 self
.assertTrue(gc
.is_tracked(set()))
449 def test_bug1055820b(self
):
450 # Corresponds to temp2b.py in the bug report.
453 def callback(ignored
):
454 ouch
[:] = [wr() for wr
in WRs
]
456 Cs
= [C1055820(i
) for i
in range(2)]
457 WRs
= [weakref
.ref(c
, callback
) for c
in Cs
]
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.
467 self
.assertEqual(len(ouch
), 2) # else the callbacks didn't run
469 # If the callback resurrected one of these guys, the instance
470 # would be damaged, with an empty __dict__.
471 self
.assertEqual(x
, None)
473 class GCTogglingTests(unittest
.TestCase
):
480 def test_bug1055820c(self
):
481 # Corresponds to temp2c.py in the bug report. This is pretty
485 # Move c0 into generation 2.
489 c1
.keep_c0_alive
= c0
490 del c0
.loop
# now only c1 keeps c0 alive
493 c2wr
= weakref
.ref(c2
) # no callback!
496 def callback(ignored
):
499 # The callback gets associated with a wr on an object in generation 2.
500 c0wr
= weakref
.ref(c0
, callback
)
504 # What we've set up: c0, c1, and c2 are all trash now. c0 is in
505 # generation 2. The only thing keeping it alive is that c1 points to
506 # it. c1 and c2 are in generation 0, and are in self-loops. There's a
507 # global weakref to c2 (c2wr), but that weakref has no callback.
508 # There's also a global weakref to c0 (c0wr), and that does have a
509 # callback, and that callback references c2 via c2wr().
511 # c0 has a wr with callback, which references c2wr
514 # | Generation 2 above dots
515 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
516 # | Generation 0 below dots
519 # ^->c1 ^->c2 has a wr but no callback
523 # So this is the nightmare: when generation 0 gets collected, we see
524 # that c2 has a callback-free weakref, and c1 doesn't even have a
525 # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
526 # the only object that has a weakref with a callback. gc clears c1
527 # and c2. Clearing c1 has the side effect of dropping the refcount on
528 # c0 to 0, so c0 goes away (despite that it's in an older generation)
529 # and c0's wr callback triggers. That in turn materializes a reference
530 # to c2 via c2wr(), but c2 gets cleared anyway by gc.
532 # We want to let gc happen "naturally", to preserve the distinction
533 # between generations.
536 detector
= GC_Detector()
537 while not detector
.gc_happened
:
540 self
.fail("gc didn't happen after 10000 iterations")
541 self
.assertEqual(len(ouch
), 0)
542 junk
.append([]) # this will eventually trigger gc
544 self
.assertEqual(len(ouch
), 1) # else the callback wasn't invoked
546 # If the callback resurrected c2, the instance would be damaged,
547 # with an empty __dict__.
548 self
.assertEqual(x
, None)
550 def test_bug1055820d(self
):
551 # Corresponds to temp2d.py in the bug report. This is very much like
552 # test_bug1055820c, but uses a __del__ method instead of a weakref
553 # callback to sneak in a resurrection of cyclic trash.
561 # Move all the above into generation 2.
565 c1
.keep_d0_alive
= d0
566 del d0
.loop
# now only c1 keeps d0 alive
569 c2wr
= weakref
.ref(c2
) # no callback!
573 # What we've set up: d0, c1, and c2 are all trash now. d0 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
576 # a global weakref to c2 (c2wr), but that weakref has no callback.
577 # There are no other weakrefs.
579 # d0 has a __del__ method that references c2wr
582 # | Generation 2 above dots
583 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
584 # | Generation 0 below dots
587 # ^->c1 ^->c2 has a wr but no callback
591 # So this is the nightmare: when generation 0 gets collected, we see
592 # that c2 has a callback-free weakref, and c1 doesn't even have a
593 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
594 # c1 and c2. Clearing c1 has the side effect of dropping the refcount
595 # on d0 to 0, so d0 goes away (despite that it's in an older
596 # generation) and d0's __del__ triggers. That in turn materializes
597 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
599 # We want to let gc happen "naturally", to preserve the distinction
600 # between generations.
601 detector
= GC_Detector()
604 while not detector
.gc_happened
:
607 self
.fail("gc didn't happen after 10000 iterations")
608 self
.assertEqual(len(ouch
), 0)
609 junk
.append([]) # this will eventually trigger gc
611 self
.assertEqual(len(ouch
), 1) # else __del__ wasn't invoked
613 # If __del__ resurrected c2, the instance would be damaged, with an
615 self
.assertEqual(x
, None)
618 enabled
= gc
.isenabled()
620 assert not gc
.isenabled()
621 debug
= gc
.get_debug()
622 gc
.set_debug(debug
& ~gc
.DEBUG_LEAK
) # this test is supposed to leak
625 gc
.collect() # Delete 2nd generation garbage
626 run_unittest(GCTests
, GCTogglingTests
)
629 # test gc.enable() even if GC is disabled by default
631 print "restoring automatic collection"
632 # make sure to always test gc.enable()
634 assert gc
.isenabled()
638 if __name__
== "__main__":