| 1 | """Weak reference support for Python.
 | 
| 2 | 
 | 
| 3 | This module is an implementation of PEP 205:
 | 
| 4 | 
 | 
| 5 | http://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 | 
 | 
| 12 | import UserDict
 | 
| 13 | 
 | 
| 14 | from _weakref import (
 | 
| 15 |      getweakrefcount,
 | 
| 16 |      getweakrefs,
 | 
| 17 |      ref,
 | 
| 18 |      proxy,
 | 
| 19 |      CallableProxyType,
 | 
| 20 |      ProxyType,
 | 
| 21 |      ReferenceType)
 | 
| 22 | 
 | 
| 23 | from _weakrefset import WeakSet, _IterationGuard
 | 
| 24 | 
 | 
| 25 | from exceptions import ReferenceError
 | 
| 26 | 
 | 
| 27 | 
 | 
| 28 | ProxyTypes = (ProxyType, CallableProxyType)
 | 
| 29 | 
 | 
| 30 | __all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
 | 
| 31 |            "WeakKeyDictionary", "ReferenceError", "ReferenceType", "ProxyType",
 | 
| 32 |            "CallableProxyType", "ProxyTypes", "WeakValueDictionary", 'WeakSet']
 | 
| 33 | 
 | 
| 34 | 
 | 
| 35 | class 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 | 
 | 
| 265 | class 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 | 
 | 
| 286 | class 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)
 |