summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_weakref.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_weakref.py')
-rw-r--r--Lib/test/test_weakref.py89
1 files changed, 88 insertions, 1 deletions
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 4313c1d..1aa3540 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -6,8 +6,10 @@ import weakref
import operator
import contextlib
import copy
+import time
-from test import support, script_helper
+from test import support
+from test.support import script_helper
# Used in ReferencesTestCase.test_ref_created_during_del() .
ref_from_del = None
@@ -71,6 +73,29 @@ class TestBase(unittest.TestCase):
self.cbcalled += 1
+@contextlib.contextmanager
+def collect_in_thread(period=0.0001):
+ """
+ Ensure GC collections happen in a different thread, at a high frequency.
+ """
+ threading = support.import_module('threading')
+ please_stop = False
+
+ def collect():
+ while not please_stop:
+ time.sleep(period)
+ gc.collect()
+
+ with support.disable_gc():
+ t = threading.Thread(target=collect)
+ t.start()
+ try:
+ yield
+ finally:
+ please_stop = True
+ t.join()
+
+
class ReferencesTestCase(TestBase):
def test_basic_ref(self):
@@ -92,6 +117,18 @@ class ReferencesTestCase(TestBase):
self.check_basic_callback(create_function)
self.check_basic_callback(create_bound_method)
+ @support.cpython_only
+ def test_cfunction(self):
+ import _testcapi
+ create_cfunction = _testcapi.create_cfunction
+ f = create_cfunction()
+ wr = weakref.ref(f)
+ self.assertIs(wr(), f)
+ del f
+ self.assertIsNone(wr())
+ self.check_basic_ref(create_cfunction)
+ self.check_basic_callback(create_cfunction)
+
def test_multiple_callbacks(self):
o = C()
ref1 = weakref.ref(o, self.callback)
@@ -120,6 +157,10 @@ class ReferencesTestCase(TestBase):
ref1 = weakref.ref(c, callback)
del c
+ def test_constructor_kwargs(self):
+ c = C()
+ self.assertRaises(TypeError, weakref.ref, c, callback=None)
+
def test_proxy_ref(self):
o = C()
o.bar = 1
@@ -572,6 +613,7 @@ class ReferencesTestCase(TestBase):
del c1, c2, C, D
gc.collect()
+ @support.requires_type_collecting
def test_callback_in_cycle_resurrection(self):
import gc
@@ -827,6 +869,14 @@ class ReferencesTestCase(TestBase):
with self.assertRaises(AttributeError):
ref1.__callback__ = lambda ref: None
+ def test_callback_gcs(self):
+ class ObjectWithDel(Object):
+ def __del__(self): pass
+ x = ObjectWithDel(1)
+ ref1 = weakref.ref(x, lambda ref: support.gc_collect())
+ del x
+ support.gc_collect()
+
class SubclassableWeakrefTestCase(TestBase):
@@ -1599,6 +1649,43 @@ class MappingTestCase(TestBase):
self.assertEqual(len(d), 0)
self.assertEqual(count, 2)
+ def test_make_weak_valued_dict_repr(self):
+ dict = weakref.WeakValueDictionary()
+ self.assertRegex(repr(dict), '<WeakValueDictionary at 0x.*>')
+
+ def test_make_weak_keyed_dict_repr(self):
+ dict = weakref.WeakKeyDictionary()
+ self.assertRegex(repr(dict), '<WeakKeyDictionary at 0x.*>')
+
+ def test_threaded_weak_valued_setdefault(self):
+ d = weakref.WeakValueDictionary()
+ with collect_in_thread():
+ for i in range(100000):
+ x = d.setdefault(10, RefCycle())
+ self.assertIsNot(x, None) # we never put None in there!
+ del x
+
+ def test_threaded_weak_valued_pop(self):
+ d = weakref.WeakValueDictionary()
+ with collect_in_thread():
+ for i in range(100000):
+ d[10] = RefCycle()
+ x = d.pop(10, 10)
+ self.assertIsNot(x, None) # we never put None in there!
+
+ def test_threaded_weak_valued_consistency(self):
+ # Issue #28427: old keys should not remove new values from
+ # WeakValueDictionary when collecting from another thread.
+ d = weakref.WeakValueDictionary()
+ with collect_in_thread():
+ for i in range(200000):
+ o = RefCycle()
+ d[10] = o
+ # o is still alive, so the dict can't be empty
+ self.assertEqual(len(d), 1)
+ o = None # lose ref
+
+
from test import mapping_tests
class WeakValueDictionaryTestCase(mapping_tests.BasicTestMappingProtocol):