mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
Issue #28427: old keys should not remove new values from
WeakValueDictionary when collecting from another thread.
This commit is contained in:
parent
1fee5151f7
commit
e10ca3a0fe
7 changed files with 175 additions and 31 deletions
|
@ -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())
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue