Issue #28427: old keys should not remove new values from
WeakValueDictionary when collecting from another thread.
diff --git a/Lib/weakref.py b/Lib/weakref.py
index def955f..c66943f 100644
--- a/Lib/weakref.py
+++ b/Lib/weakref.py
@@ -18,7 +18,8 @@
proxy,
CallableProxyType,
ProxyType,
- ReferenceType)
+ ReferenceType,
+ _remove_dead_weakref)
from _weakrefset import WeakSet, _IterationGuard
@@ -58,7 +59,9 @@
if self._iterating:
self._pending_removals.append(wr.key)
else:
- del self.data[wr.key]
+ # Atomic removal is necessary since this function
+ # can be called asynchronously by the GC
+ _remove_dead_weakref(self.data, wr.key)
self._remove = remove
# A list of keys to be removed
self._pending_removals = []
@@ -71,9 +74,12 @@
# We shouldn't encounter any KeyError, because this method should
# always be called *before* mutating the dict.
while l:
- del d[l.pop()]
+ key = l.pop()
+ _remove_dead_weakref(d, key)
def __getitem__(self, key):
+ if self._pending_removals:
+ self._commit_removals()
o = self.data[key]()
if o is None:
raise KeyError, key
@@ -86,6 +92,8 @@
del self.data[key]
def __contains__(self, key):
+ if self._pending_removals:
+ self._commit_removals()
try:
o = self.data[key]()
except KeyError:
@@ -93,6 +101,8 @@
return o is not None
def has_key(self, key):
+ if self._pending_removals:
+ self._commit_removals()
try:
o = self.data[key]()
except KeyError:
@@ -113,6 +123,8 @@
self.data.clear()
def copy(self):
+ if self._pending_removals:
+ self._commit_removals()
new = WeakValueDictionary()
for key, wr in self.data.items():
o = wr()
@@ -124,6 +136,8 @@
def __deepcopy__(self, memo):
from copy import deepcopy
+ if self._pending_removals:
+ self._commit_removals()
new = self.__class__()
for key, wr in self.data.items():
o = wr()
@@ -132,6 +146,8 @@
return new
def get(self, key, default=None):
+ if self._pending_removals:
+ self._commit_removals()
try:
wr = self.data[key]
except KeyError:
@@ -145,6 +161,8 @@
return o
def items(self):
+ if self._pending_removals:
+ self._commit_removals()
L = []
for key, wr in self.data.items():
o = wr()
@@ -153,6 +171,8 @@
return L
def iteritems(self):
+ if self._pending_removals:
+ self._commit_removals()
with _IterationGuard(self):
for wr in self.data.itervalues():
value = wr()
@@ -160,6 +180,8 @@
yield wr.key, value
def iterkeys(self):
+ if self._pending_removals:
+ self._commit_removals()
with _IterationGuard(self):
for k in self.data.iterkeys():
yield k
@@ -176,11 +198,15 @@
keep the values around longer than needed.
"""
+ if self._pending_removals:
+ self._commit_removals()
with _IterationGuard(self):
for wr in self.data.itervalues():
yield wr
def itervalues(self):
+ if self._pending_removals:
+ self._commit_removals()
with _IterationGuard(self):
for wr in self.data.itervalues():
obj = wr()
@@ -212,13 +238,13 @@
return o
def setdefault(self, key, default=None):
+ if self._pending_removals:
+ self._commit_removals()
try:
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:
@@ -254,9 +280,13 @@
keep the values around longer than needed.
"""
+ if self._pending_removals:
+ self._commit_removals()
return self.data.values()
def values(self):
+ if self._pending_removals:
+ self._commit_removals()
L = []
for wr in self.data.values():
o = wr()