summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorHugo van Kemenade <1324225+hugovk@users.noreply.github.com>2024-11-19 09:25:09 (GMT)
committerGitHub <noreply@github.com>2024-11-19 09:25:09 (GMT)
commit899fdb213db6c5881c5f9c6760ead6fd713d2070 (patch)
treec4e291504c73e8055d02176551beeb39ab47e6ad /Lib/test
parent84f07c3a4cbcfe488ccfb4030571be0bc4de7e45 (diff)
downloadcpython-899fdb213db6c5881c5f9c6760ead6fd713d2070.zip
cpython-899fdb213db6c5881c5f9c6760ead6fd713d2070.tar.gz
cpython-899fdb213db6c5881c5f9c6760ead6fd713d2070.tar.bz2
Revert "GH-126491: GC: Mark objects reachable from roots before doing cycle collection (GH-126502)" (#126983)
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_dict.py109
-rw-r--r--Lib/test/test_gc.py33
2 files changed, 128 insertions, 14 deletions
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index c94dc2d..4030716 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -880,6 +880,115 @@ class DictTest(unittest.TestCase):
gc.collect()
self.assertIs(ref(), None, "Cycle was not collected")
+ def _not_tracked(self, t):
+ # Nested containers can take several collections to untrack
+ gc.collect()
+ gc.collect()
+ self.assertFalse(gc.is_tracked(t), t)
+
+ def _tracked(self, t):
+ self.assertTrue(gc.is_tracked(t), t)
+ gc.collect()
+ gc.collect()
+ self.assertTrue(gc.is_tracked(t), t)
+
+ def test_string_keys_can_track_values(self):
+ # Test that this doesn't leak.
+ for i in range(10):
+ d = {}
+ for j in range(10):
+ d[str(j)] = j
+ d["foo"] = d
+
+ @support.cpython_only
+ def test_track_literals(self):
+ # Test GC-optimization of dict literals
+ x, y, z, w = 1.5, "a", (1, None), []
+
+ self._not_tracked({})
+ self._not_tracked({x:(), y:x, z:1})
+ self._not_tracked({1: "a", "b": 2})
+ self._not_tracked({1: 2, (None, True, False, ()): int})
+ self._not_tracked({1: object()})
+
+ # Dicts with mutable elements are always tracked, even if those
+ # elements are not tracked right now.
+ self._tracked({1: []})
+ self._tracked({1: ([],)})
+ self._tracked({1: {}})
+ self._tracked({1: set()})
+
+ @support.cpython_only
+ def test_track_dynamic(self):
+ # Test GC-optimization of dynamically-created dicts
+ class MyObject(object):
+ pass
+ x, y, z, w, o = 1.5, "a", (1, object()), [], MyObject()
+
+ d = dict()
+ self._not_tracked(d)
+ d[1] = "a"
+ self._not_tracked(d)
+ d[y] = 2
+ self._not_tracked(d)
+ d[z] = 3
+ self._not_tracked(d)
+ self._not_tracked(d.copy())
+ d[4] = w
+ self._tracked(d)
+ self._tracked(d.copy())
+ d[4] = None
+ self._not_tracked(d)
+ self._not_tracked(d.copy())
+
+ # dd isn't tracked right now, but it may mutate and therefore d
+ # which contains it must be tracked.
+ d = dict()
+ dd = dict()
+ d[1] = dd
+ self._not_tracked(dd)
+ self._tracked(d)
+ dd[1] = d
+ self._tracked(dd)
+
+ d = dict.fromkeys([x, y, z])
+ self._not_tracked(d)
+ dd = dict()
+ dd.update(d)
+ self._not_tracked(dd)
+ d = dict.fromkeys([x, y, z, o])
+ self._tracked(d)
+ dd = dict()
+ dd.update(d)
+ self._tracked(dd)
+
+ d = dict(x=x, y=y, z=z)
+ self._not_tracked(d)
+ d = dict(x=x, y=y, z=z, w=w)
+ self._tracked(d)
+ d = dict()
+ d.update(x=x, y=y, z=z)
+ self._not_tracked(d)
+ d.update(w=w)
+ self._tracked(d)
+
+ d = dict([(x, y), (z, 1)])
+ self._not_tracked(d)
+ d = dict([(x, y), (z, w)])
+ self._tracked(d)
+ d = dict()
+ d.update([(x, y), (z, 1)])
+ self._not_tracked(d)
+ d.update([(x, y), (z, w)])
+ self._tracked(d)
+
+ @support.cpython_only
+ def test_track_subtypes(self):
+ # Dict subtypes are always tracked
+ class MyDict(dict):
+ pass
+ self._tracked(MyDict())
+
def make_shared_key_dict(self, n):
class C:
pass
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index e02ec7c..0372815 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -31,11 +31,6 @@ except ImportError:
return C
ContainerNoGC = None
-try:
- import _testinternalcapi
-except ImportError:
- _testinternalcapi = None
-
### Support code
###############################################################################
@@ -1135,7 +1130,6 @@ class IncrementalGCTests(unittest.TestCase):
def tearDown(self):
gc.disable()
- @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
@requires_gil_enabled("Free threading does not support incremental GC")
# Use small increments to emulate longer running process in a shorter time
@gc_threshold(200, 10)
@@ -1161,18 +1155,32 @@ class IncrementalGCTests(unittest.TestCase):
return head
head = make_ll(1000)
+ count = 1000
+
+ # There will be some objects we aren't counting,
+ # e.g. the gc stats dicts. This test checks
+ # that the counts don't grow, so we try to
+ # correct for the uncounted objects
+ # This is just an estimate.
+ CORRECTION = 20
enabled = gc.isenabled()
gc.enable()
olds = []
- initial_heap_size = _testinternalcapi.get_heap_size()
for i in range(20_000):
newhead = make_ll(20)
+ count += 20
newhead.surprise = head
olds.append(newhead)
if len(olds) == 20:
- new_objects = _testinternalcapi.get_heap_size() - initial_heap_size
- self.assertLess(new_objects, 25_000)
+ stats = gc.get_stats()
+ young = stats[0]
+ incremental = stats[1]
+ old = stats[2]
+ collected = young['collected'] + incremental['collected'] + old['collected']
+ count += CORRECTION
+ live = count - collected
+ self.assertLess(live, 25000)
del olds[:]
if not enabled:
gc.disable()
@@ -1314,8 +1322,7 @@ class GCCallbackTests(unittest.TestCase):
from test.support import gc_collect, SuppressCrashReport
a = [1, 2, 3]
- b = [a, a]
- a.append(b)
+ b = [a]
# Avoid coredump when Py_FatalError() calls abort()
SuppressCrashReport().__enter__()
@@ -1325,8 +1332,6 @@ class GCCallbackTests(unittest.TestCase):
# (to avoid deallocating it):
import ctypes
ctypes.pythonapi.Py_DecRef(ctypes.py_object(a))
- del a
- del b
# The garbage collector should now have a fatal error
# when it reaches the broken object
@@ -1355,7 +1360,7 @@ class GCCallbackTests(unittest.TestCase):
self.assertRegex(stderr,
br'object type name: list')
self.assertRegex(stderr,
- br'object repr : \[1, 2, 3, \[\[...\], \[...\]\]\]')
+ br'object repr : \[1, 2, 3\]')
class GCTogglingTests(unittest.TestCase):