summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_weakref.py
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2012-03-01 15:26:35 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2012-03-01 15:26:35 (GMT)
commitbbe2f60b3c19ecaa02ca07be14474eaacfcb59a0 (patch)
tree768a0d9c5d1402c0d91dcaf9597e83adea6d892a /Lib/test/test_weakref.py
parenteb977dac9cfd590982d08d1a9f7bae58498648ca (diff)
downloadcpython-bbe2f60b3c19ecaa02ca07be14474eaacfcb59a0.zip
cpython-bbe2f60b3c19ecaa02ca07be14474eaacfcb59a0.tar.gz
cpython-bbe2f60b3c19ecaa02ca07be14474eaacfcb59a0.tar.bz2
Issue #14159: Fix the len() of weak containers (WeakSet, WeakKeyDictionary, WeakValueDictionary) to return a better approximation when some objects are dead or dying.
Moreover, the implementation is now O(1) rather than O(n). Thanks to Yury Selivanov for reporting.
Diffstat (limited to 'Lib/test/test_weakref.py')
-rw-r--r--Lib/test/test_weakref.py60
1 files changed, 60 insertions, 0 deletions
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 8b5bbc3..74b9a87 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -812,11 +812,71 @@ class Object:
def __hash__(self):
return hash(self.arg)
+class RefCycle:
+ def __init__(self):
+ self.cycle = self
+
class MappingTestCase(TestBase):
COUNT = 10
+ def check_len_cycles(self, dict_type, cons):
+ N = 20
+ items = [RefCycle() for i in range(N)]
+ dct = dict_type(cons(o) for o in items)
+ # Keep an iterator alive
+ it = dct.items()
+ try:
+ next(it)
+ except StopIteration:
+ pass
+ del items
+ gc.collect()
+ n1 = len(dct)
+ del it
+ gc.collect()
+ n2 = len(dct)
+ # one item may be kept alive inside the iterator
+ self.assertIn(n1, (0, 1))
+ self.assertEqual(n2, 0)
+
+ def test_weak_keyed_len_cycles(self):
+ self.check_len_cycles(weakref.WeakKeyDictionary, lambda k: (k, 1))
+
+ def test_weak_valued_len_cycles(self):
+ self.check_len_cycles(weakref.WeakValueDictionary, lambda k: (1, k))
+
+ def check_len_race(self, dict_type, cons):
+ # Extended sanity checks for len() in the face of cyclic collection
+ self.addCleanup(gc.set_threshold, *gc.get_threshold())
+ for th in range(1, 100):
+ N = 20
+ gc.collect(0)
+ gc.set_threshold(th, th, th)
+ items = [RefCycle() for i in range(N)]
+ dct = dict_type(cons(o) for o in items)
+ del items
+ # All items will be collected at next garbage collection pass
+ it = dct.items()
+ try:
+ next(it)
+ except StopIteration:
+ pass
+ n1 = len(dct)
+ del it
+ n2 = len(dct)
+ self.assertGreaterEqual(n1, 0)
+ self.assertLessEqual(n1, N)
+ self.assertGreaterEqual(n2, 0)
+ self.assertLessEqual(n2, n1)
+
+ def test_weak_keyed_len_race(self):
+ self.check_len_race(weakref.WeakKeyDictionary, lambda k: (k, 1))
+
+ def test_weak_valued_len_race(self):
+ self.check_len_race(weakref.WeakValueDictionary, lambda k: (1, k))
+
def test_weak_values(self):
#
# This exercises d.copy(), d.items(), d[], del d[], len(d).