diff -r 651aa21433ba Lib/test/test_weakref.py --- a/Lib/test/test_weakref.py Mon Feb 02 17:47:31 2015 -0500 +++ b/Lib/test/test_weakref.py Tue Feb 03 00:36:14 2015 +0100 @@ -1594,6 +1594,34 @@ dict = weakref.WeakKeyDictionary() self.assertRegex(repr(dict), '') + def test_issue19542(self): + import _thread, gc, time + + def f(): + while running: + time.sleep(0.01) + gc.collect() + + class C: pass + class D: pass + d = weakref.WeakValueDictionary() + + running = True + _thread.start_new_thread(f, ()) + + i = 10**5 + while i > 0: + i -= 1 + x = d.setdefault(10, D()) + assert x is not None # we never put None in there! + tp = x.__class__ + del x + if tp is D: + c = C(); c.cycle = c + d[10] = c + c = None + running = False + from test import mapping_tests class WeakValueDictionaryTestCase(mapping_tests.BasicTestMappingProtocol): diff -r 651aa21433ba Lib/weakref.py --- a/Lib/weakref.py Mon Feb 02 17:47:31 2015 -0500 +++ b/Lib/weakref.py Tue Feb 03 00:36:14 2015 +0100 @@ -233,24 +233,26 @@ try: o = self.data.pop(key)() except KeyError: + o = None + if o is None: if args: return args[0] - raise - if o is None: raise KeyError(key) else: return o def setdefault(self, key, default=None): try: - wr = self.data[key] + o = self.data[key]() except KeyError: + o = None + if o is None: if self._pending_removals: self._commit_removals() self.data[key] = KeyedRef(default, self._remove, key) return default else: - return wr() + return o def update(self, dict=None, **kwargs): if self._pending_removals: