Issue #28427: old keys should not remove new values from

WeakValueDictionary when collecting from another thread.
This commit is contained in:
Antoine Pitrou 2016-12-27 14:19:20 +01:00
parent 1fee5151f7
commit e10ca3a0fe
7 changed files with 175 additions and 31 deletions

View file

@ -16,7 +16,8 @@ from _weakref import (
proxy,
CallableProxyType,
ProxyType,
ReferenceType)
ReferenceType,
_remove_dead_weakref)
from _weakrefset import WeakSet, _IterationGuard
@ -111,7 +112,9 @@ class WeakValueDictionary(collections.MutableMapping):
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(d, wr.key)
self._remove = remove
# A list of keys to be removed
self._pending_removals = []
@ -125,9 +128,12 @@ class WeakValueDictionary(collections.MutableMapping):
# 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)
@ -140,9 +146,13 @@ class WeakValueDictionary(collections.MutableMapping):
del self.data[key]
def __len__(self):
return len(self.data) - len(self._pending_removals)
if self._pending_removals:
self._commit_removals()
return len(self.data)
def __contains__(self, key):
if self._pending_removals:
self._commit_removals()
try:
o = self.data[key]()
except KeyError:
@ -158,6 +168,8 @@ class WeakValueDictionary(collections.MutableMapping):
self.data[key] = KeyedRef(value, self._remove, key)
def copy(self):
if self._pending_removals:
self._commit_removals()
new = WeakValueDictionary()
for key, wr in self.data.items():
o = wr()
@ -169,6 +181,8 @@ class WeakValueDictionary(collections.MutableMapping):
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()
@ -177,6 +191,8 @@ class WeakValueDictionary(collections.MutableMapping):
return new
def get(self, key, default=None):
if self._pending_removals:
self._commit_removals()
try:
wr = self.data[key]
except KeyError:
@ -190,6 +206,8 @@ class WeakValueDictionary(collections.MutableMapping):
return o
def items(self):
if self._pending_removals:
self._commit_removals()
with _IterationGuard(self):
for k, wr in self.data.items():
v = wr()
@ -197,6 +215,8 @@ class WeakValueDictionary(collections.MutableMapping):
yield k, v
def keys(self):
if self._pending_removals:
self._commit_removals()
with _IterationGuard(self):
for k, wr in self.data.items():
if wr() is not None:
@ -214,10 +234,14 @@ class WeakValueDictionary(collections.MutableMapping):
keep the values around longer than needed.
"""
if self._pending_removals:
self._commit_removals()
with _IterationGuard(self):
yield from self.data.values()
def values(self):
if self._pending_removals:
self._commit_removals()
with _IterationGuard(self):
for wr in self.data.values():
obj = wr()
@ -290,6 +314,8 @@ class WeakValueDictionary(collections.MutableMapping):
keep the values around longer than needed.
"""
if self._pending_removals:
self._commit_removals()
return list(self.data.values())