diff options
author | Tim Peters <tim.peters@gmail.com> | 2019-10-09 17:37:30 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-09 17:37:30 (GMT) |
commit | ecbf35f9335b0420cb8adfda6f299d6747a16515 (patch) | |
tree | 3f011932f3e968a91391a6931346825596525f60 /Lib | |
parent | 01171ebd966b0cd6352057799ad876dd1e07942e (diff) | |
download | cpython-ecbf35f9335b0420cb8adfda6f299d6747a16515.zip cpython-ecbf35f9335b0420cb8adfda6f299d6747a16515.tar.gz cpython-ecbf35f9335b0420cb8adfda6f299d6747a16515.tar.bz2 |
bpo-38379: don't claim objects are collected when they aren't (#16658)
* bpo-38379: when a finalizer resurrects an object,
nothing is actually collected in this run of gc.
Change the stats to relect that truth.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_gc.py | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 8215390..f52db1e 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -822,6 +822,76 @@ class GCTests(unittest.TestCase): self.assertRaises(TypeError, gc.get_objects, "1") self.assertRaises(TypeError, gc.get_objects, 1.234) + def test_38379(self): + # When a finalizer resurrects objects, stats were reporting them as + # having been collected. This affected both collect()'s return + # value and the dicts returned by get_stats(). + N = 100 + + class A: # simple self-loop + def __init__(self): + self.me = self + + class Z(A): # resurrecting __del__ + def __del__(self): + zs.append(self) + + zs = [] + + def getstats(): + d = gc.get_stats()[-1] + return d['collected'], d['uncollectable'] + + gc.collect() + gc.disable() + + # No problems if just collecting A() instances. + oldc, oldnc = getstats() + for i in range(N): + A() + t = gc.collect() + c, nc = getstats() + self.assertEqual(t, 2*N) # instance object & its dict + self.assertEqual(c - oldc, 2*N) + self.assertEqual(nc - oldnc, 0) + + # But Z() is not actually collected. + oldc, oldnc = c, nc + Z() + # Nothing is collected - Z() is merely resurrected. + t = gc.collect() + c, nc = getstats() + #self.assertEqual(t, 2) # before + self.assertEqual(t, 0) # after + #self.assertEqual(c - oldc, 2) # before + self.assertEqual(c - oldc, 0) # after + self.assertEqual(nc - oldnc, 0) + + # Unfortunately, a Z() prevents _anything_ from being collected. + # It should be possible to collect the A instances anyway, but + # that will require non-trivial code changes. + oldc, oldnc = c, nc + for i in range(N): + A() + Z() + # Z() prevents anything from being collected. + t = gc.collect() + c, nc = getstats() + #self.assertEqual(t, 2*N + 2) # before + self.assertEqual(t, 0) # after + #self.assertEqual(c - oldc, 2*N + 2) # before + self.assertEqual(c - oldc, 0) # after + self.assertEqual(nc - oldnc, 0) + + # But the A() trash is reclaimed on the next run. + oldc, oldnc = c, nc + t = gc.collect() + c, nc = getstats() + self.assertEqual(t, 2*N) + self.assertEqual(c - oldc, 2*N) + self.assertEqual(nc - oldnc, 0) + + gc.enable() class GCCallbackTests(unittest.TestCase): def setUp(self): |