2
from 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", 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.assertEqual
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] + list(range(5)))
416
self.assertEqual(gc.get_referents(1, 'a', 4j), [])
418
def test_bug1055820b(self):
419
# Corresponds to temp2b.py in the bug report.
422
def callback(ignored):
423
ouch[:] = [wr() for wr in WRs]
425
Cs = [C1055820(i) for i in range(2)]
426
WRs = [weakref.ref(c, callback) for c in Cs]
430
self.assertEqual(len(ouch), 0)
431
# Make the two instances trash, and collect again. The bug was that
432
# the callback materialized a strong reference to an instance, but gc
433
# cleared the instance's dict anyway.
436
self.assertEqual(len(ouch), 2) # else the callbacks didn't run
438
# If the callback resurrected one of these guys, the instance
439
# would be damaged, with an empty __dict__.
440
self.assertEqual(x, None)
442
class GCTogglingTests(unittest.TestCase):
449
def test_bug1055820c(self):
450
# Corresponds to temp2c.py in the bug report. This is pretty
454
# Move c0 into generation 2.
458
c1.keep_c0_alive = c0
459
del c0.loop # now only c1 keeps c0 alive
462
c2wr = weakref.ref(c2) # no callback!
465
def callback(ignored):
468
# The callback gets associated with a wr on an object in generation 2.
469
c0wr = weakref.ref(c0, callback)
473
# What we've set up: c0, c1, and c2 are all trash now. c0 is in
474
# generation 2. The only thing keeping it alive is that c1 points to
475
# it. c1 and c2 are in generation 0, and are in self-loops. There's a
476
# global weakref to c2 (c2wr), but that weakref has no callback.
477
# There's also a global weakref to c0 (c0wr), and that does have a
478
# callback, and that callback references c2 via c2wr().
480
# c0 has a wr with callback, which references c2wr
483
# | Generation 2 above dots
484
#. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
485
# | Generation 0 below dots
488
# ^->c1 ^->c2 has a wr but no callback
492
# So this is the nightmare: when generation 0 gets collected, we see
493
# that c2 has a callback-free weakref, and c1 doesn't even have a
494
# weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
495
# the only object that has a weakref with a callback. gc clears c1
496
# and c2. Clearing c1 has the side effect of dropping the refcount on
497
# c0 to 0, so c0 goes away (despite that it's in an older generation)
498
# and c0's wr callback triggers. That in turn materializes a reference
499
# to c2 via c2wr(), but c2 gets cleared anyway by gc.
501
# We want to let gc happen "naturally", to preserve the distinction
502
# between generations.
505
detector = GC_Detector()
506
while not detector.gc_happened:
509
self.fail("gc didn't happen after 10000 iterations")
510
self.assertEqual(len(ouch), 0)
511
junk.append([]) # this will eventually trigger gc
513
self.assertEqual(len(ouch), 1) # else the callback wasn't invoked
515
# If the callback resurrected c2, the instance would be damaged,
516
# with an empty __dict__.
517
self.assertEqual(x, None)
519
def test_bug1055820d(self):
520
# Corresponds to temp2d.py in the bug report. This is very much like
521
# test_bug1055820c, but uses a __del__ method instead of a weakref
522
# callback to sneak in a resurrection of cyclic trash.
530
# Move all the above into generation 2.
534
c1.keep_d0_alive = d0
535
del d0.loop # now only c1 keeps d0 alive
538
c2wr = weakref.ref(c2) # no callback!
542
# What we've set up: d0, c1, and c2 are all trash now. d0 is in
543
# generation 2. The only thing keeping it alive is that c1 points to
544
# it. c1 and c2 are in generation 0, and are in self-loops. There's
545
# a global weakref to c2 (c2wr), but that weakref has no callback.
546
# There are no other weakrefs.
548
# d0 has a __del__ method that references c2wr
551
# | Generation 2 above dots
552
#. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
553
# | Generation 0 below dots
556
# ^->c1 ^->c2 has a wr but no callback
560
# So this is the nightmare: when generation 0 gets collected, we see
561
# that c2 has a callback-free weakref, and c1 doesn't even have a
562
# weakref. Collecting generation 0 doesn't see d0 at all. gc clears
563
# c1 and c2. Clearing c1 has the side effect of dropping the refcount
564
# on d0 to 0, so d0 goes away (despite that it's in an older
565
# generation) and d0's __del__ triggers. That in turn materializes
566
# a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
568
# We want to let gc happen "naturally", to preserve the distinction
569
# between generations.
570
detector = GC_Detector()
573
while not detector.gc_happened:
576
self.fail("gc didn't happen after 10000 iterations")
577
self.assertEqual(len(ouch), 0)
578
junk.append([]) # this will eventually trigger gc
580
self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked
582
# If __del__ resurrected c2, the instance would be damaged, with an
584
self.assertEqual(x, None)
587
enabled = gc.isenabled()
589
assert not gc.isenabled()
590
debug = gc.get_debug()
591
gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
594
gc.collect() # Delete 2nd generation garbage
595
run_unittest(GCTests, GCTogglingTests)
598
# test gc.enable() even if GC is disabled by default
600
print("restoring automatic collection")
601
# make sure to always test gc.enable()
603
assert gc.isenabled()
607
if __name__ == "__main__":