diff options
Diffstat (limited to 'Lib/weakref.py')
| -rw-r--r-- | Lib/weakref.py | 218 |
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: |
