bpo-38379: don't claim objects are collected when they aren't (#16658) · python/cpython@ecbf35f (original) (raw)
`@@ -822,6 +822,76 @@ def test_get_objects_arguments(self):
`
822
822
`self.assertRaises(TypeError, gc.get_objects, "1")
`
823
823
`self.assertRaises(TypeError, gc.get_objects, 1.234)
`
824
824
``
``
825
`+
def test_38379(self):
`
``
826
`+
When a finalizer resurrects objects, stats were reporting them as
`
``
827
`+
having been collected. This affected both collect()'s return
`
``
828
`+
value and the dicts returned by get_stats().
`
``
829
`+
N = 100
`
``
830
+
``
831
`+
class A: # simple self-loop
`
``
832
`+
def init(self):
`
``
833
`+
self.me = self
`
``
834
+
``
835
`+
class Z(A): # resurrecting del
`
``
836
`+
def del(self):
`
``
837
`+
zs.append(self)
`
``
838
+
``
839
`+
zs = []
`
``
840
+
``
841
`+
def getstats():
`
``
842
`+
d = gc.get_stats()[-1]
`
``
843
`+
return d['collected'], d['uncollectable']
`
``
844
+
``
845
`+
gc.collect()
`
``
846
`+
gc.disable()
`
``
847
+
``
848
`+
No problems if just collecting A() instances.
`
``
849
`+
oldc, oldnc = getstats()
`
``
850
`+
for i in range(N):
`
``
851
`+
A()
`
``
852
`+
t = gc.collect()
`
``
853
`+
c, nc = getstats()
`
``
854
`+
self.assertEqual(t, 2*N) # instance object & its dict
`
``
855
`+
self.assertEqual(c - oldc, 2*N)
`
``
856
`+
self.assertEqual(nc - oldnc, 0)
`
``
857
+
``
858
`+
But Z() is not actually collected.
`
``
859
`+
oldc, oldnc = c, nc
`
``
860
`+
Z()
`
``
861
`+
Nothing is collected - Z() is merely resurrected.
`
``
862
`+
t = gc.collect()
`
``
863
`+
c, nc = getstats()
`
``
864
`+
#self.assertEqual(t, 2) # before
`
``
865
`+
self.assertEqual(t, 0) # after
`
``
866
`+
#self.assertEqual(c - oldc, 2) # before
`
``
867
`+
self.assertEqual(c - oldc, 0) # after
`
``
868
`+
self.assertEqual(nc - oldnc, 0)
`
``
869
+
``
870
`+
Unfortunately, a Z() prevents anything from being collected.
`
``
871
`+
It should be possible to collect the A instances anyway, but
`
``
872
`+
that will require non-trivial code changes.
`
``
873
`+
oldc, oldnc = c, nc
`
``
874
`+
for i in range(N):
`
``
875
`+
A()
`
``
876
`+
Z()
`
``
877
`+
Z() prevents anything from being collected.
`
``
878
`+
t = gc.collect()
`
``
879
`+
c, nc = getstats()
`
``
880
`+
#self.assertEqual(t, 2*N + 2) # before
`
``
881
`+
self.assertEqual(t, 0) # after
`
``
882
`+
#self.assertEqual(c - oldc, 2*N + 2) # before
`
``
883
`+
self.assertEqual(c - oldc, 0) # after
`
``
884
`+
self.assertEqual(nc - oldnc, 0)
`
``
885
+
``
886
`+
But the A() trash is reclaimed on the next run.
`
``
887
`+
oldc, oldnc = c, nc
`
``
888
`+
t = gc.collect()
`
``
889
`+
c, nc = getstats()
`
``
890
`+
self.assertEqual(t, 2*N)
`
``
891
`+
self.assertEqual(c - oldc, 2*N)
`
``
892
`+
self.assertEqual(nc - oldnc, 0)
`
``
893
+
``
894
`+
gc.enable()
`
825
895
``
826
896
`class GCCallbackTests(unittest.TestCase):
`
827
897
`def setUp(self):
`