1"""Weak reference support for Python. 2 3This module is an implementation of PEP 205: 4 5http://www.python.org/dev/peps/pep-0205/ 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 _weakrefset import WeakSet, _IterationGuard 24 25from exceptions import ReferenceError 26 27 28ProxyTypes = (ProxyType, CallableProxyType) 29 30__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs", 31 "WeakKeyDictionary", "ReferenceError", "ReferenceType", "ProxyType", 32 "CallableProxyType", "ProxyTypes", "WeakValueDictionary", 'WeakSet'] 33 34 35class WeakValueDictionary(UserDict.UserDict): 36 """Mapping class that references values weakly. 37 38 Entries in the dictionary will be discarded when no strong 39 reference to the value exists anymore 40 """ 41 # We inherit the constructor without worrying about the input 42 # dictionary; since it uses our .update() method, we get the right 43 # checks (if the other dictionary is a WeakValueDictionary, 44 # objects are unwrapped on the way out, and we always wrap on the 45 # way in). 46 47 def __init__(self, *args, **kw): 48 def remove(wr, selfref=ref(self)): 49 self = selfref() 50 if self is not None: 51 if self._iterating: 52 self._pending_removals.append(wr.key) 53 else: 54 del self.data[wr.key] 55 self._remove = remove 56 # A list of keys to be removed 57 self._pending_removals = [] 58 self._iterating = set() 59 UserDict.UserDict.__init__(self, *args, **kw) 60 61 def _commit_removals(self): 62 l = self._pending_removals 63 d = self.data 64 # We shouldn't encounter any KeyError, because this method should 65 # always be called *before* mutating the dict. 66 while l: 67 del d[l.pop()] 68 69 def __getitem__(self, key): 70 o = self.data[key]() 71 if o is None: 72 raise KeyError, key 73 else: 74 return o 75 76 def __delitem__(self, key): 77 if self._pending_removals: 78 self._commit_removals() 79 del self.data[key] 80 81 def __contains__(self, key): 82 try: 83 o = self.data[key]() 84 except KeyError: 85 return False 86 return o is not None 87 88 def has_key(self, key): 89 try: 90 o = self.data[key]() 91 except KeyError: 92 return False 93 return o is not None 94 95 def __repr__(self): 96 return "<WeakValueDictionary at %s>" % id(self) 97 98 def __setitem__(self, key, value): 99 if self._pending_removals: 100 self._commit_removals() 101 self.data[key] = KeyedRef(value, self._remove, key) 102 103 def clear(self): 104 if self._pending_removals: 105 self._commit_removals() 106 self.data.clear() 107 108 def copy(self): 109 new = WeakValueDictionary() 110 for key, wr in self.data.items(): 111 o = wr() 112 if o is not None: 113 new[key] = o 114 return new 115 116 __copy__ = copy 117 118 def __deepcopy__(self, memo): 119 from copy import deepcopy 120 new = self.__class__() 121 for key, wr in self.data.items(): 122 o = wr() 123 if o is not None: 124 new[deepcopy(key, memo)] = o 125 return new 126 127 def get(self, key, default=None): 128 try: 129 wr = self.data[key] 130 except KeyError: 131 return default 132 else: 133 o = wr() 134 if o is None: 135 # This should only happen 136 return default 137 else: 138 return o 139 140 def items(self): 141 L = [] 142 for key, wr in self.data.items(): 143 o = wr() 144 if o is not None: 145 L.append((key, o)) 146 return L 147 148 def iteritems(self): 149 with _IterationGuard(self): 150 for wr in self.data.itervalues(): 151 value = wr() 152 if value is not None: 153 yield wr.key, value 154 155 def iterkeys(self): 156 with _IterationGuard(self): 157 for k in self.data.iterkeys(): 158 yield k 159 160 __iter__ = iterkeys 161 162 def itervaluerefs(self): 163 """Return an iterator that yields the weak references to the values. 164 165 The references are not guaranteed to be 'live' at the time 166 they are used, so the result of calling the references needs 167 to be checked before being used. This can be used to avoid 168 creating references that will cause the garbage collector to 169 keep the values around longer than needed. 170 171 """ 172 with _IterationGuard(self): 173 for wr in self.data.itervalues(): 174 yield wr 175 176 def itervalues(self): 177 with _IterationGuard(self): 178 for wr in self.data.itervalues(): 179 obj = wr() 180 if obj is not None: 181 yield obj 182 183 def popitem(self): 184 if self._pending_removals: 185 self._commit_removals() 186 while 1: 187 key, wr = self.data.popitem() 188 o = wr() 189 if o is not None: 190 return key, o 191 192 def pop(self, key, *args): 193 if self._pending_removals: 194 self._commit_removals() 195 try: 196 o = self.data.pop(key)() 197 except KeyError: 198 if args: 199 return args[0] 200 raise 201 if o is None: 202 raise KeyError, key 203 else: 204 return o 205 206 def setdefault(self, key, default=None): 207 try: 208 wr = self.data[key] 209 except KeyError: 210 if self._pending_removals: 211 self._commit_removals() 212 self.data[key] = KeyedRef(default, self._remove, key) 213 return default 214 else: 215 return wr() 216 217 def update(self, dict=None, **kwargs): 218 if self._pending_removals: 219 self._commit_removals() 220 d = self.data 221 if dict is not None: 222 if not hasattr(dict, "items"): 223 dict = type({})(dict) 224 for key, o in dict.items(): 225 d[key] = KeyedRef(o, self._remove, key) 226 if len(kwargs): 227 self.update(kwargs) 228 229 def valuerefs(self): 230 """Return a list of weak references to the values. 231 232 The references are not guaranteed to be 'live' at the time 233 they are used, so the result of calling the references needs 234 to be checked before being used. This can be used to avoid 235 creating references that will cause the garbage collector to 236 keep the values around longer than needed. 237 238 """ 239 return self.data.values() 240 241 def values(self): 242 L = [] 243 for wr in self.data.values(): 244 o = wr() 245 if o is not None: 246 L.append(o) 247 return L 248 249 250class KeyedRef(ref): 251 """Specialized reference that includes a key corresponding to the value. 252 253 This is used in the WeakValueDictionary to avoid having to create 254 a function object for each key stored in the mapping. A shared 255 callback object can use the 'key' attribute of a KeyedRef instead 256 of getting a reference to the key from an enclosing scope. 257 258 """ 259 260 __slots__ = "key", 261 262 def __new__(type, ob, callback, key): 263 self = ref.__new__(type, ob, callback) 264 self.key = key 265 return self 266 267 def __init__(self, ob, callback, key): 268 super(KeyedRef, self).__init__(ob, callback) 269 270 271class WeakKeyDictionary(UserDict.UserDict): 272 """ Mapping class that references keys weakly. 273 274 Entries in the dictionary will be discarded when there is no 275 longer a strong reference to the key. This can be used to 276 associate additional data with an object owned by other parts of 277 an application without adding attributes to those objects. This 278 can be especially useful with objects that override attribute 279 accesses. 280 """ 281 282 def __init__(self, dict=None): 283 self.data = {} 284 def remove(k, selfref=ref(self)): 285 self = selfref() 286 if self is not None: 287 if self._iterating: 288 self._pending_removals.append(k) 289 else: 290 del self.data[k] 291 self._remove = remove 292 # A list of dead weakrefs (keys to be removed) 293 self._pending_removals = [] 294 self._iterating = set() 295 if dict is not None: 296 self.update(dict) 297 298 def _commit_removals(self): 299 # NOTE: We don't need to call this method before mutating the dict, 300 # because a dead weakref never compares equal to a live weakref, 301 # even if they happened to refer to equal objects. 302 # However, it means keys may already have been removed. 303 l = self._pending_removals 304 d = self.data 305 while l: 306 try: 307 del d[l.pop()] 308 except KeyError: 309 pass 310 311 def __delitem__(self, key): 312 del self.data[ref(key)] 313 314 def __getitem__(self, key): 315 return self.data[ref(key)] 316 317 def __repr__(self): 318 return "<WeakKeyDictionary at %s>" % id(self) 319 320 def __setitem__(self, key, value): 321 self.data[ref(key, self._remove)] = value 322 323 def copy(self): 324 new = WeakKeyDictionary() 325 for key, value in self.data.items(): 326 o = key() 327 if o is not None: 328 new[o] = value 329 return new 330 331 __copy__ = copy 332 333 def __deepcopy__(self, memo): 334 from copy import deepcopy 335 new = self.__class__() 336 for key, value in self.data.items(): 337 o = key() 338 if o is not None: 339 new[o] = deepcopy(value, memo) 340 return new 341 342 def get(self, key, default=None): 343 return self.data.get(ref(key),default) 344 345 def has_key(self, key): 346 try: 347 wr = ref(key) 348 except TypeError: 349 return 0 350 return wr in self.data 351 352 def __contains__(self, key): 353 try: 354 wr = ref(key) 355 except TypeError: 356 return 0 357 return wr in self.data 358 359 def items(self): 360 L = [] 361 for key, value in self.data.items(): 362 o = key() 363 if o is not None: 364 L.append((o, value)) 365 return L 366 367 def iteritems(self): 368 with _IterationGuard(self): 369 for wr, value in self.data.iteritems(): 370 key = wr() 371 if key is not None: 372 yield key, value 373 374 def iterkeyrefs(self): 375 """Return an iterator that yields the weak references to the keys. 376 377 The references are not guaranteed to be 'live' at the time 378 they are used, so the result of calling the references needs 379 to be checked before being used. This can be used to avoid 380 creating references that will cause the garbage collector to 381 keep the keys around longer than needed. 382 383 """ 384 with _IterationGuard(self): 385 for wr in self.data.iterkeys(): 386 yield wr 387 388 def iterkeys(self): 389 with _IterationGuard(self): 390 for wr in self.data.iterkeys(): 391 obj = wr() 392 if obj is not None: 393 yield obj 394 395 __iter__ = iterkeys 396 397 def itervalues(self): 398 with _IterationGuard(self): 399 for value in self.data.itervalues(): 400 yield value 401 402 def keyrefs(self): 403 """Return a list of weak references to the keys. 404 405 The references are not guaranteed to be 'live' at the time 406 they are used, so the result of calling the references needs 407 to be checked before being used. This can be used to avoid 408 creating references that will cause the garbage collector to 409 keep the keys around longer than needed. 410 411 """ 412 return self.data.keys() 413 414 def keys(self): 415 L = [] 416 for wr in self.data.keys(): 417 o = wr() 418 if o is not None: 419 L.append(o) 420 return L 421 422 def popitem(self): 423 while 1: 424 key, value = self.data.popitem() 425 o = key() 426 if o is not None: 427 return o, value 428 429 def pop(self, key, *args): 430 return self.data.pop(ref(key), *args) 431 432 def setdefault(self, key, default=None): 433 return self.data.setdefault(ref(key, self._remove),default) 434 435 def update(self, dict=None, **kwargs): 436 d = self.data 437 if dict is not None: 438 if not hasattr(dict, "items"): 439 dict = type({})(dict) 440 for key, value in dict.items(): 441 d[ref(key, self._remove)] = value 442 if len(kwargs): 443 self.update(kwargs) 444