weakref.py revision 61146790992e0a00b76a6bf6ecc9e53289a1ecd7
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 __init__(self, *args, **kw): 46 UserDict.UserDict.__init__(self, *args, **kw) 47 def remove(wr, selfref=ref(self)): 48 self = selfref() 49 if self is not None: 50 del self.data[wr.key] 51 self._remove = remove 52 53 def __getitem__(self, key): 54 o = self.data[key]() 55 if o is None: 56 raise KeyError, key 57 else: 58 return o 59 60 def __contains__(self, key): 61 try: 62 o = self.data[key]() 63 except KeyError: 64 return False 65 return o is not None 66 67 def has_key(self, key): 68 try: 69 o = self.data[key]() 70 except KeyError: 71 return False 72 return o is not None 73 74 def __repr__(self): 75 return "<WeakValueDictionary at %s>" % id(self) 76 77 def __setitem__(self, key, value): 78 self.data[key] = KeyedRef(value, self._remove, key) 79 80 def copy(self): 81 new = WeakValueDictionary() 82 for key, wr in self.data.items(): 83 o = wr() 84 if o is not None: 85 new[key] = o 86 return new 87 88 def get(self, key, default=None): 89 try: 90 wr = self.data[key] 91 except KeyError: 92 return default 93 else: 94 o = wr() 95 if o is None: 96 # This should only happen 97 return default 98 else: 99 return o 100 101 def items(self): 102 L = [] 103 for key, wr in self.data.items(): 104 o = wr() 105 if o is not None: 106 L.append((key, o)) 107 return L 108 109 def iteritems(self): 110 for wr in self.data.itervalues(): 111 value = wr() 112 if value is not None: 113 yield wr.key, value 114 115 def iterkeys(self): 116 return self.data.iterkeys() 117 118 def __iter__(self): 119 return self.data.iterkeys() 120 121 def itervalues(self): 122 for wr in self.data.itervalues(): 123 obj = wr() 124 if obj is not None: 125 yield obj 126 127 def popitem(self): 128 while 1: 129 key, wr = self.data.popitem() 130 o = wr() 131 if o is not None: 132 return key, o 133 134 def pop(self, key, *args): 135 try: 136 o = self.data.pop(key)() 137 except KeyError: 138 if args: 139 return args[0] 140 raise 141 if o is None: 142 raise KeyError, key 143 else: 144 return o 145 146 def setdefault(self, key, default=None): 147 try: 148 wr = self.data[key] 149 except KeyError: 150 self.data[key] = KeyedRef(default, self._remove, key) 151 return default 152 else: 153 return wr() 154 155 def update(self, dict=None, **kwargs): 156 d = self.data 157 if dict is not None: 158 if not hasattr(dict, "items"): 159 dict = type({})(dict) 160 for key, o in dict.items(): 161 d[key] = KeyedRef(o, self._remove, key) 162 if len(kwargs): 163 self.update(kwargs) 164 165 def values(self): 166 L = [] 167 for wr in self.data.values(): 168 o = wr() 169 if o is not None: 170 L.append(o) 171 return L 172 173 174class KeyedRef(ref): 175 """Specialized reference that includes a key corresponding to the value. 176 177 This is used in the WeakValueDictionary to avoid having to create 178 a function object for each key stored in the mapping. A shared 179 callback object can use the 'key' attribute of a KeyedRef instead 180 of getting a reference to the key from an enclosing scope. 181 182 """ 183 184 __slots__ = "key", 185 186 def __new__(type, ob, callback, key): 187 self = ref.__new__(type, ob, callback) 188 self.key = key 189 return self 190 191 def __init__(self, ob, callback, key): 192 super(KeyedRef, self).__init__(ob, callback) 193 194 195class WeakKeyDictionary(UserDict.UserDict): 196 """ Mapping class that references keys weakly. 197 198 Entries in the dictionary will be discarded when there is no 199 longer a strong reference to the key. This can be used to 200 associate additional data with an object owned by other parts of 201 an application without adding attributes to those objects. This 202 can be especially useful with objects that override attribute 203 accesses. 204 """ 205 206 def __init__(self, dict=None): 207 self.data = {} 208 def remove(k, selfref=ref(self)): 209 self = selfref() 210 if self is not None: 211 del self.data[k] 212 self._remove = remove 213 if dict is not None: self.update(dict) 214 215 def __delitem__(self, key): 216 del self.data[ref(key)] 217 218 def __getitem__(self, key): 219 return self.data[ref(key)] 220 221 def __repr__(self): 222 return "<WeakKeyDictionary at %s>" % id(self) 223 224 def __setitem__(self, key, value): 225 self.data[ref(key, self._remove)] = value 226 227 def copy(self): 228 new = WeakKeyDictionary() 229 for key, value in self.data.items(): 230 o = key() 231 if o is not None: 232 new[o] = value 233 return new 234 235 def get(self, key, default=None): 236 return self.data.get(ref(key),default) 237 238 def has_key(self, key): 239 try: 240 wr = ref(key) 241 except TypeError: 242 return 0 243 return wr in self.data 244 245 def __contains__(self, key): 246 try: 247 wr = ref(key) 248 except TypeError: 249 return 0 250 return wr in self.data 251 252 def items(self): 253 L = [] 254 for key, value in self.data.items(): 255 o = key() 256 if o is not None: 257 L.append((o, value)) 258 return L 259 260 def iteritems(self): 261 for wr, value in self.data.iteritems(): 262 key = wr() 263 if key is not None: 264 yield key, value 265 266 def iterkeys(self): 267 for wr in self.data.iterkeys(): 268 obj = wr() 269 if obj is not None: 270 yield obj 271 272 def __iter__(self): 273 return self.iterkeys() 274 275 def itervalues(self): 276 return self.data.itervalues() 277 278 def keys(self): 279 L = [] 280 for wr in self.data.keys(): 281 o = wr() 282 if o is not None: 283 L.append(o) 284 return L 285 286 def popitem(self): 287 while 1: 288 key, value = self.data.popitem() 289 o = key() 290 if o is not None: 291 return o, value 292 293 def pop(self, key, *args): 294 return self.data.pop(ref(key), *args) 295 296 def setdefault(self, key, default=None): 297 return self.data.setdefault(ref(key, self._remove),default) 298 299 def update(self, dict=None, **kwargs): 300 d = self.data 301 if dict is not None: 302 if not hasattr(dict, "items"): 303 dict = type({})(dict) 304 for key, value in dict.items(): 305 d[ref(key, self._remove)] = value 306 if len(kwargs): 307 self.update(kwargs) 308