+++ /dev/null
-"""Weak reference support for Python.\r
-\r
-This module is an implementation of PEP 205:\r
-\r
-http://www.python.org/dev/peps/pep-0205/\r
-"""\r
-\r
-# Naming convention: Variables named "wr" are weak reference objects;\r
-# they are called this instead of "ref" to avoid name collisions with\r
-# the module-global ref() function imported from _weakref.\r
-\r
-import UserDict\r
-\r
-from _weakref import (\r
- getweakrefcount,\r
- getweakrefs,\r
- ref,\r
- proxy,\r
- CallableProxyType,\r
- ProxyType,\r
- ReferenceType)\r
-\r
-from _weakrefset import WeakSet, _IterationGuard\r
-\r
-from exceptions import ReferenceError\r
-\r
-\r
-ProxyTypes = (ProxyType, CallableProxyType)\r
-\r
-__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",\r
- "WeakKeyDictionary", "ReferenceError", "ReferenceType", "ProxyType",\r
- "CallableProxyType", "ProxyTypes", "WeakValueDictionary", 'WeakSet']\r
-\r
-\r
-class WeakValueDictionary(UserDict.UserDict):\r
- """Mapping class that references values weakly.\r
-\r
- Entries in the dictionary will be discarded when no strong\r
- reference to the value exists anymore\r
- """\r
- # We inherit the constructor without worrying about the input\r
- # dictionary; since it uses our .update() method, we get the right\r
- # checks (if the other dictionary is a WeakValueDictionary,\r
- # objects are unwrapped on the way out, and we always wrap on the\r
- # way in).\r
-\r
- def __init__(self, *args, **kw):\r
- def remove(wr, selfref=ref(self)):\r
- self = selfref()\r
- if self is not None:\r
- if self._iterating:\r
- self._pending_removals.append(wr.key)\r
- else:\r
- del self.data[wr.key]\r
- self._remove = remove\r
- # A list of keys to be removed\r
- self._pending_removals = []\r
- self._iterating = set()\r
- UserDict.UserDict.__init__(self, *args, **kw)\r
-\r
- def _commit_removals(self):\r
- l = self._pending_removals\r
- d = self.data\r
- # We shouldn't encounter any KeyError, because this method should\r
- # always be called *before* mutating the dict.\r
- while l:\r
- del d[l.pop()]\r
-\r
- def __getitem__(self, key):\r
- o = self.data[key]()\r
- if o is None:\r
- raise KeyError, key\r
- else:\r
- return o\r
-\r
- def __delitem__(self, key):\r
- if self._pending_removals:\r
- self._commit_removals()\r
- del self.data[key]\r
-\r
- def __contains__(self, key):\r
- try:\r
- o = self.data[key]()\r
- except KeyError:\r
- return False\r
- return o is not None\r
-\r
- def has_key(self, key):\r
- try:\r
- o = self.data[key]()\r
- except KeyError:\r
- return False\r
- return o is not None\r
-\r
- def __repr__(self):\r
- return "<WeakValueDictionary at %s>" % id(self)\r
-\r
- def __setitem__(self, key, value):\r
- if self._pending_removals:\r
- self._commit_removals()\r
- self.data[key] = KeyedRef(value, self._remove, key)\r
-\r
- def clear(self):\r
- if self._pending_removals:\r
- self._commit_removals()\r
- self.data.clear()\r
-\r
- def copy(self):\r
- new = WeakValueDictionary()\r
- for key, wr in self.data.items():\r
- o = wr()\r
- if o is not None:\r
- new[key] = o\r
- return new\r
-\r
- __copy__ = copy\r
-\r
- def __deepcopy__(self, memo):\r
- from copy import deepcopy\r
- new = self.__class__()\r
- for key, wr in self.data.items():\r
- o = wr()\r
- if o is not None:\r
- new[deepcopy(key, memo)] = o\r
- return new\r
-\r
- def get(self, key, default=None):\r
- try:\r
- wr = self.data[key]\r
- except KeyError:\r
- return default\r
- else:\r
- o = wr()\r
- if o is None:\r
- # This should only happen\r
- return default\r
- else:\r
- return o\r
-\r
- def items(self):\r
- L = []\r
- for key, wr in self.data.items():\r
- o = wr()\r
- if o is not None:\r
- L.append((key, o))\r
- return L\r
-\r
- def iteritems(self):\r
- with _IterationGuard(self):\r
- for wr in self.data.itervalues():\r
- value = wr()\r
- if value is not None:\r
- yield wr.key, value\r
-\r
- def iterkeys(self):\r
- with _IterationGuard(self):\r
- for k in self.data.iterkeys():\r
- yield k\r
-\r
- __iter__ = iterkeys\r
-\r
- def itervaluerefs(self):\r
- """Return an iterator that yields the weak references to the values.\r
-\r
- The references are not guaranteed to be 'live' at the time\r
- they are used, so the result of calling the references needs\r
- to be checked before being used. This can be used to avoid\r
- creating references that will cause the garbage collector to\r
- keep the values around longer than needed.\r
-\r
- """\r
- with _IterationGuard(self):\r
- for wr in self.data.itervalues():\r
- yield wr\r
-\r
- def itervalues(self):\r
- with _IterationGuard(self):\r
- for wr in self.data.itervalues():\r
- obj = wr()\r
- if obj is not None:\r
- yield obj\r
-\r
- def popitem(self):\r
- if self._pending_removals:\r
- self._commit_removals()\r
- while 1:\r
- key, wr = self.data.popitem()\r
- o = wr()\r
- if o is not None:\r
- return key, o\r
-\r
- def pop(self, key, *args):\r
- if self._pending_removals:\r
- self._commit_removals()\r
- try:\r
- o = self.data.pop(key)()\r
- except KeyError:\r
- if args:\r
- return args[0]\r
- raise\r
- if o is None:\r
- raise KeyError, key\r
- else:\r
- return o\r
-\r
- def setdefault(self, key, default=None):\r
- try:\r
- wr = self.data[key]\r
- except KeyError:\r
- if self._pending_removals:\r
- self._commit_removals()\r
- self.data[key] = KeyedRef(default, self._remove, key)\r
- return default\r
- else:\r
- return wr()\r
-\r
- def update(self, dict=None, **kwargs):\r
- if self._pending_removals:\r
- self._commit_removals()\r
- d = self.data\r
- if dict is not None:\r
- if not hasattr(dict, "items"):\r
- dict = type({})(dict)\r
- for key, o in dict.items():\r
- d[key] = KeyedRef(o, self._remove, key)\r
- if len(kwargs):\r
- self.update(kwargs)\r
-\r
- def valuerefs(self):\r
- """Return a list of weak references to the values.\r
-\r
- The references are not guaranteed to be 'live' at the time\r
- they are used, so the result of calling the references needs\r
- to be checked before being used. This can be used to avoid\r
- creating references that will cause the garbage collector to\r
- keep the values around longer than needed.\r
-\r
- """\r
- return self.data.values()\r
-\r
- def values(self):\r
- L = []\r
- for wr in self.data.values():\r
- o = wr()\r
- if o is not None:\r
- L.append(o)\r
- return L\r
-\r
-\r
-class KeyedRef(ref):\r
- """Specialized reference that includes a key corresponding to the value.\r
-\r
- This is used in the WeakValueDictionary to avoid having to create\r
- a function object for each key stored in the mapping. A shared\r
- callback object can use the 'key' attribute of a KeyedRef instead\r
- of getting a reference to the key from an enclosing scope.\r
-\r
- """\r
-\r
- __slots__ = "key",\r
-\r
- def __new__(type, ob, callback, key):\r
- self = ref.__new__(type, ob, callback)\r
- self.key = key\r
- return self\r
-\r
- def __init__(self, ob, callback, key):\r
- super(KeyedRef, self).__init__(ob, callback)\r
-\r
-\r
-class WeakKeyDictionary(UserDict.UserDict):\r
- """ Mapping class that references keys weakly.\r
-\r
- Entries in the dictionary will be discarded when there is no\r
- longer a strong reference to the key. This can be used to\r
- associate additional data with an object owned by other parts of\r
- an application without adding attributes to those objects. This\r
- can be especially useful with objects that override attribute\r
- accesses.\r
- """\r
-\r
- def __init__(self, dict=None):\r
- self.data = {}\r
- def remove(k, selfref=ref(self)):\r
- self = selfref()\r
- if self is not None:\r
- if self._iterating:\r
- self._pending_removals.append(k)\r
- else:\r
- del self.data[k]\r
- self._remove = remove\r
- # A list of dead weakrefs (keys to be removed)\r
- self._pending_removals = []\r
- self._iterating = set()\r
- if dict is not None:\r
- self.update(dict)\r
-\r
- def _commit_removals(self):\r
- # NOTE: We don't need to call this method before mutating the dict,\r
- # because a dead weakref never compares equal to a live weakref,\r
- # even if they happened to refer to equal objects.\r
- # However, it means keys may already have been removed.\r
- l = self._pending_removals\r
- d = self.data\r
- while l:\r
- try:\r
- del d[l.pop()]\r
- except KeyError:\r
- pass\r
-\r
- def __delitem__(self, key):\r
- del self.data[ref(key)]\r
-\r
- def __getitem__(self, key):\r
- return self.data[ref(key)]\r
-\r
- def __repr__(self):\r
- return "<WeakKeyDictionary at %s>" % id(self)\r
-\r
- def __setitem__(self, key, value):\r
- self.data[ref(key, self._remove)] = value\r
-\r
- def copy(self):\r
- new = WeakKeyDictionary()\r
- for key, value in self.data.items():\r
- o = key()\r
- if o is not None:\r
- new[o] = value\r
- return new\r
-\r
- __copy__ = copy\r
-\r
- def __deepcopy__(self, memo):\r
- from copy import deepcopy\r
- new = self.__class__()\r
- for key, value in self.data.items():\r
- o = key()\r
- if o is not None:\r
- new[o] = deepcopy(value, memo)\r
- return new\r
-\r
- def get(self, key, default=None):\r
- return self.data.get(ref(key),default)\r
-\r
- def has_key(self, key):\r
- try:\r
- wr = ref(key)\r
- except TypeError:\r
- return 0\r
- return wr in self.data\r
-\r
- def __contains__(self, key):\r
- try:\r
- wr = ref(key)\r
- except TypeError:\r
- return 0\r
- return wr in self.data\r
-\r
- def items(self):\r
- L = []\r
- for key, value in self.data.items():\r
- o = key()\r
- if o is not None:\r
- L.append((o, value))\r
- return L\r
-\r
- def iteritems(self):\r
- with _IterationGuard(self):\r
- for wr, value in self.data.iteritems():\r
- key = wr()\r
- if key is not None:\r
- yield key, value\r
-\r
- def iterkeyrefs(self):\r
- """Return an iterator that yields the weak references to the keys.\r
-\r
- The references are not guaranteed to be 'live' at the time\r
- they are used, so the result of calling the references needs\r
- to be checked before being used. This can be used to avoid\r
- creating references that will cause the garbage collector to\r
- keep the keys around longer than needed.\r
-\r
- """\r
- with _IterationGuard(self):\r
- for wr in self.data.iterkeys():\r
- yield wr\r
-\r
- def iterkeys(self):\r
- with _IterationGuard(self):\r
- for wr in self.data.iterkeys():\r
- obj = wr()\r
- if obj is not None:\r
- yield obj\r
-\r
- __iter__ = iterkeys\r
-\r
- def itervalues(self):\r
- with _IterationGuard(self):\r
- for value in self.data.itervalues():\r
- yield value\r
-\r
- def keyrefs(self):\r
- """Return a list of weak references to the keys.\r
-\r
- The references are not guaranteed to be 'live' at the time\r
- they are used, so the result of calling the references needs\r
- to be checked before being used. This can be used to avoid\r
- creating references that will cause the garbage collector to\r
- keep the keys around longer than needed.\r
-\r
- """\r
- return self.data.keys()\r
-\r
- def keys(self):\r
- L = []\r
- for wr in self.data.keys():\r
- o = wr()\r
- if o is not None:\r
- L.append(o)\r
- return L\r
-\r
- def popitem(self):\r
- while 1:\r
- key, value = self.data.popitem()\r
- o = key()\r
- if o is not None:\r
- return o, value\r
-\r
- def pop(self, key, *args):\r
- return self.data.pop(ref(key), *args)\r
-\r
- def setdefault(self, key, default=None):\r
- return self.data.setdefault(ref(key, self._remove),default)\r
-\r
- def update(self, dict=None, **kwargs):\r
- d = self.data\r
- if dict is not None:\r
- if not hasattr(dict, "items"):\r
- dict = type({})(dict)\r
- for key, value in dict.items():\r
- d[ref(key, self._remove)] = value\r
- if len(kwargs):\r
- self.update(kwargs)\r