summaryrefslogtreecommitdiffstats
path: root/Lib/weakref.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/weakref.py')
-rw-r--r--Lib/weakref.py218
1 files changed, 112 insertions, 106 deletions
diff --git a/Lib/weakref.py b/Lib/weakref.py
index 88c60e7..fcb6b74 100644
--- a/Lib/weakref.py
+++ b/Lib/weakref.py
@@ -9,8 +9,6 @@ http://www.python.org/dev/peps/pep-0205/
# they are called this instead of "ref" to avoid name collisions with
# the module-global ref() function imported from _weakref.
-import UserDict
-
from _weakref import (
getweakrefcount,
getweakrefs,
@@ -20,19 +18,19 @@ from _weakref import (
ProxyType,
ReferenceType)
-from _weakrefset import WeakSet
-
-from exceptions import ReferenceError
+from _weakrefset import WeakSet, _IterationGuard
+import collections # Import after _weakref to avoid circular import.
ProxyTypes = (ProxyType, CallableProxyType)
__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
- "WeakKeyDictionary", "ReferenceError", "ReferenceType", "ProxyType",
- "CallableProxyType", "ProxyTypes", "WeakValueDictionary", 'WeakSet']
+ "WeakKeyDictionary", "ReferenceType", "ProxyType",
+ "CallableProxyType", "ProxyTypes", "WeakValueDictionary",
+ "WeakSet"]
-class WeakValueDictionary(UserDict.UserDict):
+class WeakValueDictionary(collections.MutableMapping):
"""Mapping class that references values weakly.
Entries in the dictionary will be discarded when no strong
@@ -48,25 +46,41 @@ class WeakValueDictionary(UserDict.UserDict):
def remove(wr, selfref=ref(self)):
self = selfref()
if self is not None:
- del self.data[wr.key]
+ if self._iterating:
+ self._pending_removals.append(wr.key)
+ else:
+ del self.data[wr.key]
self._remove = remove
- UserDict.UserDict.__init__(self, *args, **kw)
+ # A list of keys to be removed
+ self._pending_removals = []
+ self._iterating = set()
+ self.data = d = {}
+ self.update(*args, **kw)
+
+ def _commit_removals(self):
+ l = self._pending_removals
+ d = self.data
+ # We shouldn't encounter any KeyError, because this method should
+ # always be called *before* mutating the dict.
+ while l:
+ del d[l.pop()]
def __getitem__(self, key):
o = self.data[key]()
if o is None:
- raise KeyError, key
+ raise KeyError(key)
else:
return o
- def __contains__(self, key):
- try:
- o = self.data[key]()
- except KeyError:
- return False
- return o is not None
+ def __delitem__(self, key):
+ if self._pending_removals:
+ self._commit_removals()
+ del self.data[key]
- def has_key(self, key):
+ def __len__(self):
+ return len(self.data) - len(self._pending_removals)
+
+ def __contains__(self, key):
try:
o = self.data[key]()
except KeyError:
@@ -77,6 +91,8 @@ class WeakValueDictionary(UserDict.UserDict):
return "<WeakValueDictionary at %s>" % id(self)
def __setitem__(self, key, value):
+ if self._pending_removals:
+ self._commit_removals()
self.data[key] = KeyedRef(value, self._remove, key)
def copy(self):
@@ -112,24 +128,19 @@ class WeakValueDictionary(UserDict.UserDict):
return o
def items(self):
- L = []
- for key, wr in self.data.items():
- o = wr()
- if o is not None:
- L.append((key, o))
- return L
-
- def iteritems(self):
- for wr in self.data.itervalues():
- value = wr()
- if value is not None:
- yield wr.key, value
+ with _IterationGuard(self):
+ for k, wr in self.data.items():
+ v = wr()
+ if v is not None:
+ yield k, v
- def iterkeys(self):
- return self.data.iterkeys()
+ def keys(self):
+ with _IterationGuard(self):
+ for k, wr in self.data.items():
+ if wr() is not None:
+ yield k
- def __iter__(self):
- return self.data.iterkeys()
+ __iter__ = keys
def itervaluerefs(self):
"""Return an iterator that yields the weak references to the values.
@@ -141,22 +152,29 @@ class WeakValueDictionary(UserDict.UserDict):
keep the values around longer than needed.
"""
- return self.data.itervalues()
+ with _IterationGuard(self):
+ for wr in self.data.values():
+ yield wr
- def itervalues(self):
- for wr in self.data.itervalues():
- obj = wr()
- if obj is not None:
- yield obj
+ def values(self):
+ with _IterationGuard(self):
+ for wr in self.data.values():
+ obj = wr()
+ if obj is not None:
+ yield obj
def popitem(self):
- while 1:
+ if self._pending_removals:
+ self._commit_removals()
+ while True:
key, wr = self.data.popitem()
o = wr()
if o is not None:
return key, o
def pop(self, key, *args):
+ if self._pending_removals:
+ self._commit_removals()
try:
o = self.data.pop(key)()
except KeyError:
@@ -164,7 +182,7 @@ class WeakValueDictionary(UserDict.UserDict):
return args[0]
raise
if o is None:
- raise KeyError, key
+ raise KeyError(key)
else:
return o
@@ -172,12 +190,16 @@ class WeakValueDictionary(UserDict.UserDict):
try:
wr = self.data[key]
except KeyError:
+ if self._pending_removals:
+ self._commit_removals()
self.data[key] = KeyedRef(default, self._remove, key)
return default
else:
return wr()
def update(self, dict=None, **kwargs):
+ if self._pending_removals:
+ self._commit_removals()
d = self.data
if dict is not None:
if not hasattr(dict, "items"):
@@ -197,15 +219,7 @@ class WeakValueDictionary(UserDict.UserDict):
keep the values around longer than needed.
"""
- return self.data.values()
-
- def values(self):
- L = []
- for wr in self.data.values():
- o = wr()
- if o is not None:
- L.append(o)
- return L
+ return list(self.data.values())
class KeyedRef(ref):
@@ -226,10 +240,10 @@ class KeyedRef(ref):
return self
def __init__(self, ob, callback, key):
- super(KeyedRef, self).__init__(ob, callback)
+ super().__init__(ob, callback)
-class WeakKeyDictionary(UserDict.UserDict):
+class WeakKeyDictionary(collections.MutableMapping):
""" Mapping class that references keys weakly.
Entries in the dictionary will be discarded when there is no
@@ -245,9 +259,29 @@ class WeakKeyDictionary(UserDict.UserDict):
def remove(k, selfref=ref(self)):
self = selfref()
if self is not None:
- del self.data[k]
+ if self._iterating:
+ self._pending_removals.append(k)
+ else:
+ del self.data[k]
self._remove = remove
- if dict is not None: self.update(dict)
+ # A list of dead weakrefs (keys to be removed)
+ self._pending_removals = []
+ self._iterating = set()
+ if dict is not None:
+ self.update(dict)
+
+ def _commit_removals(self):
+ # NOTE: We don't need to call this method before mutating the dict,
+ # because a dead weakref never compares equal to a live weakref,
+ # even if they happened to refer to equal objects.
+ # However, it means keys may already have been removed.
+ l = self._pending_removals
+ d = self.data
+ while l:
+ try:
+ del d[l.pop()]
+ except KeyError:
+ pass
def __delitem__(self, key):
del self.data[ref(key)]
@@ -255,6 +289,9 @@ class WeakKeyDictionary(UserDict.UserDict):
def __getitem__(self, key):
return self.data[ref(key)]
+ def __len__(self):
+ return len(self.data) - len(self._pending_removals)
+
def __repr__(self):
return "<WeakKeyDictionary at %s>" % id(self)
@@ -283,57 +320,34 @@ class WeakKeyDictionary(UserDict.UserDict):
def get(self, key, default=None):
return self.data.get(ref(key),default)
- def has_key(self, key):
- try:
- wr = ref(key)
- except TypeError:
- return 0
- return wr in self.data
-
def __contains__(self, key):
try:
wr = ref(key)
except TypeError:
- return 0
+ return False
return wr in self.data
def items(self):
- L = []
- for key, value in self.data.items():
- o = key()
- if o is not None:
- L.append((o, value))
- return L
-
- def iteritems(self):
- for wr, value in self.data.iteritems():
- key = wr()
- if key is not None:
- yield key, value
+ with _IterationGuard(self):
+ for wr, value in self.data.items():
+ key = wr()
+ if key is not None:
+ yield key, value
- def iterkeyrefs(self):
- """Return an iterator that yields the weak references to the keys.
-
- The references are not guaranteed to be 'live' at the time
- they are used, so the result of calling the references needs
- to be checked before being used. This can be used to avoid
- creating references that will cause the garbage collector to
- keep the keys around longer than needed.
-
- """
- return self.data.iterkeys()
-
- def iterkeys(self):
- for wr in self.data.iterkeys():
- obj = wr()
- if obj is not None:
- yield obj
+ def keys(self):
+ with _IterationGuard(self):
+ for wr in self.data:
+ obj = wr()
+ if obj is not None:
+ yield obj
- def __iter__(self):
- return self.iterkeys()
+ __iter__ = keys
- def itervalues(self):
- return self.data.itervalues()
+ def values(self):
+ with _IterationGuard(self):
+ for wr, value in self.data.items():
+ if wr() is not None:
+ yield value
def keyrefs(self):
"""Return a list of weak references to the keys.
@@ -345,18 +359,10 @@ class WeakKeyDictionary(UserDict.UserDict):
keep the keys around longer than needed.
"""
- return self.data.keys()
-
- def keys(self):
- L = []
- for wr in self.data.keys():
- o = wr()
- if o is not None:
- L.append(o)
- return L
+ return list(self.data)
def popitem(self):
- while 1:
+ while True:
key, value = self.data.popitem()
o = key()
if o is not None: