weakref.py revision 886128f4f8b30b7e3623418eab063a3a8dd3495c
1"""Weak reference support for Python. 2 3This module is an implementation of PEP 205: 4 5http://python.sourceforge.net/peps/pep-0205.html 6""" 7 8# Naming convention: Variables named "wr" are weak reference objects; 9# they are called this instead of "ref" to avoid name collisions with 10# the module-global ref() function imported from _weakref. 11 12import UserDict 13 14from _weakref import \ 15 getweakrefcount, \ 16 getweakrefs, \ 17 ref, \ 18 proxy, \ 19 CallableProxyType, \ 20 ProxyType, \ 21 ReferenceType 22 23from exceptions import ReferenceError 24 25 26ProxyTypes = (ProxyType, CallableProxyType) 27 28__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs", 29 "WeakKeyDictionary", "ReferenceType", "ProxyType", 30 "CallableProxyType", "ProxyTypes", "WeakValueDictionary"] 31 32 33class WeakValueDictionary(UserDict.UserDict): 34 """Mapping class that references values weakly. 35 36 Entries in the dictionary will be discarded when no strong 37 reference to the value exists anymore 38 """ 39 # We inherit the constructor without worrying about the input 40 # dictionary; since it uses our .update() method, we get the right 41 # checks (if the other dictionary is a WeakValueDictionary, 42 # objects are unwrapped on the way out, and we always wrap on the 43 # way in). 44 45 def __getitem__(self, key): 46 o = self.data[key]() 47 if o is None: 48 raise KeyError, key 49 else: 50 return o 51 52 def __repr__(self): 53 return "<WeakValueDictionary at %s>" % id(self) 54 55 def __setitem__(self, key, value): 56 self.data[key] = ref(value, self.__makeremove(key)) 57 58 def copy(self): 59 new = WeakValueDictionary() 60 for key, wr in self.data.items(): 61 o = wr() 62 if o is not None: 63 new[key] = o 64 return new 65 66 def get(self, key, default=None): 67 try: 68 wr = self.data[key] 69 except KeyError: 70 return default 71 else: 72 o = wr() 73 if o is None: 74 # This should only happen 75 return default 76 else: 77 return o 78 79 def items(self): 80 L = [] 81 for key, wr in self.data.items(): 82 o = wr() 83 if o is not None: 84 L.append((key, o)) 85 return L 86 87 def iteritems(self): 88 return WeakValuedItemIterator(self) 89 90 def iterkeys(self): 91 return self.data.iterkeys() 92 __iter__ = iterkeys 93 94 def itervalues(self): 95 return WeakValuedValueIterator(self) 96 97 def popitem(self): 98 while 1: 99 key, wr = self.data.popitem() 100 o = wr() 101 if o is not None: 102 return key, o 103 104 def pop(self, key, *args): 105 try: 106 o = self.data.pop(key)() 107 except KeyError: 108 if args: 109 return args[0] 110 raise 111 if o is None: 112 raise KeyError, key 113 else: 114 return o 115 116 def setdefault(self, key, default): 117 try: 118 wr = self.data[key] 119 except KeyError: 120 self.data[key] = ref(default, self.__makeremove(key)) 121 return default 122 else: 123 return wr() 124 125 def update(self, dict): 126 d = self.data 127 for key, o in dict.items(): 128 d[key] = ref(o, self.__makeremove(key)) 129 130 def values(self): 131 L = [] 132 for wr in self.data.values(): 133 o = wr() 134 if o is not None: 135 L.append(o) 136 return L 137 138 def __makeremove(self, key): 139 def remove(o, selfref=ref(self), key=key): 140 self = selfref() 141 if self is not None: 142 del self.data[key] 143 return remove 144 145 146class WeakKeyDictionary(UserDict.UserDict): 147 """ Mapping class that references keys weakly. 148 149 Entries in the dictionary will be discarded when there is no 150 longer a strong reference to the key. This can be used to 151 associate additional data with an object owned by other parts of 152 an application without adding attributes to those objects. This 153 can be especially useful with objects that override attribute 154 accesses. 155 """ 156 157 def __init__(self, dict=None): 158 self.data = {} 159 def remove(k, selfref=ref(self)): 160 self = selfref() 161 if self is not None: 162 del self.data[k] 163 self._remove = remove 164 if dict is not None: self.update(dict) 165 166 def __delitem__(self, key): 167 del self.data[ref(key)] 168 169 def __getitem__(self, key): 170 return self.data[ref(key)] 171 172 def __repr__(self): 173 return "<WeakKeyDictionary at %s>" % id(self) 174 175 def __setitem__(self, key, value): 176 self.data[ref(key, self._remove)] = value 177 178 def copy(self): 179 new = WeakKeyDictionary() 180 for key, value in self.data.items(): 181 o = key() 182 if o is not None: 183 new[o] = value 184 return new 185 186 def get(self, key, default=None): 187 return self.data.get(ref(key),default) 188 189 def has_key(self, key): 190 try: 191 wr = ref(key) 192 except TypeError: 193 return 0 194 return wr in self.data 195 196 def __contains__(self, key): 197 try: 198 wr = ref(key) 199 except TypeError: 200 return 0 201 return wr in self.data 202 203 def items(self): 204 L = [] 205 for key, value in self.data.items(): 206 o = key() 207 if o is not None: 208 L.append((o, value)) 209 return L 210 211 def iteritems(self): 212 return WeakKeyedItemIterator(self) 213 214 def iterkeys(self): 215 return WeakKeyedKeyIterator(self) 216 __iter__ = iterkeys 217 218 def itervalues(self): 219 return self.data.itervalues() 220 221 def keys(self): 222 L = [] 223 for wr in self.data.keys(): 224 o = wr() 225 if o is not None: 226 L.append(o) 227 return L 228 229 def popitem(self): 230 while 1: 231 key, value = self.data.popitem() 232 o = key() 233 if o is not None: 234 return o, value 235 236 def pop(self, key, *args): 237 return self.data.pop(ref(key), *args) 238 239 def setdefault(self, key, default): 240 return self.data.setdefault(ref(key, self._remove),default) 241 242 def update(self, dict): 243 d = self.data 244 for key, value in dict.items(): 245 d[ref(key, self._remove)] = value 246 247 248class BaseIter: 249 def __iter__(self): 250 return self 251 252 253class WeakKeyedKeyIterator(BaseIter): 254 def __init__(self, weakdict): 255 self._next = weakdict.data.iterkeys().next 256 257 def next(self): 258 while 1: 259 wr = self._next() 260 obj = wr() 261 if obj is not None: 262 return obj 263 264 265class WeakKeyedItemIterator(BaseIter): 266 def __init__(self, weakdict): 267 self._next = weakdict.data.iteritems().next 268 269 def next(self): 270 while 1: 271 wr, value = self._next() 272 key = wr() 273 if key is not None: 274 return key, value 275 276 277class WeakValuedValueIterator(BaseIter): 278 def __init__(self, weakdict): 279 self._next = weakdict.data.itervalues().next 280 281 def next(self): 282 while 1: 283 wr = self._next() 284 obj = wr() 285 if obj is not None: 286 return obj 287 288 289class WeakValuedItemIterator(BaseIter): 290 def __init__(self, weakdict): 291 self._next = weakdict.data.iteritems().next 292 293 def next(self): 294 while 1: 295 key, wr = self._next() 296 value = wr() 297 if value is not None: 298 return key, value 299 300 301# no longer needed 302del UserDict 303