[3.7] bpo-38379: don't claim objects are collected when they aren't … · python/cpython@a081e93 (original) (raw)
`@@ -755,6 +755,77 @@ def test_freeze(self):
`
755
755
`gc.unfreeze()
`
756
756
`self.assertEqual(gc.get_freeze_count(), 0)
`
757
757
``
``
758
`+
def test_38379(self):
`
``
759
`+
When a finalizer resurrects objects, stats were reporting them as
`
``
760
`+
having been collected. This affected both collect()'s return
`
``
761
`+
value and the dicts returned by get_stats().
`
``
762
`+
N = 100
`
``
763
+
``
764
`+
class A: # simple self-loop
`
``
765
`+
def init(self):
`
``
766
`+
self.me = self
`
``
767
+
``
768
`+
class Z(A): # resurrecting del
`
``
769
`+
def del(self):
`
``
770
`+
zs.append(self)
`
``
771
+
``
772
`+
zs = []
`
``
773
+
``
774
`+
def getstats():
`
``
775
`+
d = gc.get_stats()[-1]
`
``
776
`+
return d['collected'], d['uncollectable']
`
``
777
+
``
778
`+
gc.collect()
`
``
779
`+
gc.disable()
`
``
780
+
``
781
`+
No problems if just collecting A() instances.
`
``
782
`+
oldc, oldnc = getstats()
`
``
783
`+
for i in range(N):
`
``
784
`+
A()
`
``
785
`+
t = gc.collect()
`
``
786
`+
c, nc = getstats()
`
``
787
`+
self.assertEqual(t, 2*N) # instance object & its dict
`
``
788
`+
self.assertEqual(c - oldc, 2*N)
`
``
789
`+
self.assertEqual(nc - oldnc, 0)
`
``
790
+
``
791
`+
But Z() is not actually collected.
`
``
792
`+
oldc, oldnc = c, nc
`
``
793
`+
Z()
`
``
794
`+
Nothing is collected - Z() is merely resurrected.
`
``
795
`+
t = gc.collect()
`
``
796
`+
c, nc = getstats()
`
``
797
`+
#self.assertEqual(t, 2) # before
`
``
798
`+
self.assertEqual(t, 0) # after
`
``
799
`+
#self.assertEqual(c - oldc, 2) # before
`
``
800
`+
self.assertEqual(c - oldc, 0) # after
`
``
801
`+
self.assertEqual(nc - oldnc, 0)
`
``
802
+
``
803
`+
Unfortunately, a Z() prevents anything from being collected.
`
``
804
`+
It should be possible to collect the A instances anyway, but
`
``
805
`+
that will require non-trivial code changes.
`
``
806
`+
oldc, oldnc = c, nc
`
``
807
`+
for i in range(N):
`
``
808
`+
A()
`
``
809
`+
Z()
`
``
810
`+
Z() prevents anything from being collected.
`
``
811
`+
t = gc.collect()
`
``
812
`+
c, nc = getstats()
`
``
813
`+
#self.assertEqual(t, 2*N + 2) # before
`
``
814
`+
self.assertEqual(t, 0) # after
`
``
815
`+
#self.assertEqual(c - oldc, 2*N + 2) # before
`
``
816
`+
self.assertEqual(c - oldc, 0) # after
`
``
817
`+
self.assertEqual(nc - oldnc, 0)
`
``
818
+
``
819
`+
But the A() trash is reclaimed on the next run.
`
``
820
`+
oldc, oldnc = c, nc
`
``
821
`+
t = gc.collect()
`
``
822
`+
c, nc = getstats()
`
``
823
`+
self.assertEqual(t, 2*N)
`
``
824
`+
self.assertEqual(c - oldc, 2*N)
`
``
825
`+
self.assertEqual(nc - oldnc, 0)
`
``
826
+
``
827
`+
gc.enable()
`
``
828
+
758
829
``
759
830
`class GCCallbackTests(unittest.TestCase):
`
760
831
`def setUp(self):
`