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