blob: cfe9456bcc2949595a74801a337eb9820c220f48 [file] [log] [blame]
Fred Drake41deb1e2001-02-01 05:27:45 +00001"""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
Fred Drakebd7f8182001-04-19 16:26:06 +00008# 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
Fred Drake41deb1e2001-02-01 05:27:45 +000012import UserDict
13
14from _weakref import \
15 getweakrefcount, \
16 getweakrefs, \
17 ref, \
18 proxy, \
Fred Drake41deb1e2001-02-01 05:27:45 +000019 CallableProxyType, \
20 ProxyType, \
21 ReferenceType
22
Fred Drakee0292422001-10-05 21:54:09 +000023from exceptions import ReferenceError
24
25
Fred Drake41deb1e2001-02-01 05:27:45 +000026ProxyTypes = (ProxyType, CallableProxyType)
27
Fred Drake9a9d2192001-04-10 19:11:23 +000028__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
Skip Montanaro40fc1602001-03-01 04:27:19 +000029 "WeakKeyDictionary", "ReferenceType", "ProxyType",
30 "CallableProxyType", "ProxyTypes", "WeakValueDictionary"]
Fred Drake41deb1e2001-02-01 05:27:45 +000031
Fred Drake41deb1e2001-02-01 05:27:45 +000032
Fred Drakebd7f8182001-04-19 16:26:06 +000033class 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 """
Fred Drake41deb1e2001-02-01 05:27:45 +000039 # We inherit the constructor without worrying about the input
40 # dictionary; since it uses our .update() method, we get the right
Fred Drake9d2c85d2001-03-01 03:06:03 +000041 # 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).
Fred Drake41deb1e2001-02-01 05:27:45 +000044
Fred Drake0a4dd392004-07-02 18:57:45 +000045 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
Fred Drake41deb1e2001-02-01 05:27:45 +000053 def __getitem__(self, key):
Fred Drake4fd06e02001-08-03 04:11:27 +000054 o = self.data[key]()
Fred Drake41deb1e2001-02-01 05:27:45 +000055 if o is None:
56 raise KeyError, key
57 else:
58 return o
59
60 def __repr__(self):
Fred Drake9d2c85d2001-03-01 03:06:03 +000061 return "<WeakValueDictionary at %s>" % id(self)
Fred Drake41deb1e2001-02-01 05:27:45 +000062
63 def __setitem__(self, key, value):
Fred Drake0a4dd392004-07-02 18:57:45 +000064 self.data[key] = KeyedRef(value, self._remove, key)
Fred Drake41deb1e2001-02-01 05:27:45 +000065
66 def copy(self):
Fred Drake9d2c85d2001-03-01 03:06:03 +000067 new = WeakValueDictionary()
Fred Drakebd7f8182001-04-19 16:26:06 +000068 for key, wr in self.data.items():
69 o = wr()
Fred Drake41deb1e2001-02-01 05:27:45 +000070 if o is not None:
71 new[key] = o
Fred Drake9d2c85d2001-03-01 03:06:03 +000072 return new
Fred Drake41deb1e2001-02-01 05:27:45 +000073
Fred Drake1d9e4b72001-04-16 17:34:48 +000074 def get(self, key, default=None):
Fred Drake41deb1e2001-02-01 05:27:45 +000075 try:
Fred Drakebd7f8182001-04-19 16:26:06 +000076 wr = self.data[key]
Fred Drake41deb1e2001-02-01 05:27:45 +000077 except KeyError:
78 return default
79 else:
Fred Drakebd7f8182001-04-19 16:26:06 +000080 o = wr()
Fred Drake41deb1e2001-02-01 05:27:45 +000081 if o is None:
82 # This should only happen
83 return default
84 else:
85 return o
86
87 def items(self):
Fred Drake312a5dc2001-02-02 15:13:24 +000088 L = []
Fred Drakebd7f8182001-04-19 16:26:06 +000089 for key, wr in self.data.items():
90 o = wr()
Fred Drake41deb1e2001-02-01 05:27:45 +000091 if o is not None:
Fred Drake312a5dc2001-02-02 15:13:24 +000092 L.append((key, o))
Fred Drake41deb1e2001-02-01 05:27:45 +000093 return L
94
Fred Drake101209d2001-05-02 05:43:09 +000095 def iteritems(self):
96 return WeakValuedItemIterator(self)
97
98 def iterkeys(self):
99 return self.data.iterkeys()
100 __iter__ = iterkeys
101
102 def itervalues(self):
103 return WeakValuedValueIterator(self)
104
Fred Drake41deb1e2001-02-01 05:27:45 +0000105 def popitem(self):
106 while 1:
Fred Drakebd7f8182001-04-19 16:26:06 +0000107 key, wr = self.data.popitem()
108 o = wr()
Fred Drake41deb1e2001-02-01 05:27:45 +0000109 if o is not None:
110 return key, o
111
Raymond Hettinger2c2d3222003-03-09 07:05:43 +0000112 def pop(self, key, *args):
113 try:
114 o = self.data.pop(key)()
115 except KeyError:
116 if args:
117 return args[0]
118 raise
119 if o is None:
120 raise KeyError, key
121 else:
122 return o
123
Walter Dörwald80ce6dd2004-05-27 18:16:25 +0000124 def setdefault(self, key, default=None):
Fred Drake41deb1e2001-02-01 05:27:45 +0000125 try:
Fred Drakebd7f8182001-04-19 16:26:06 +0000126 wr = self.data[key]
Fred Drake41deb1e2001-02-01 05:27:45 +0000127 except KeyError:
Fred Drake0a4dd392004-07-02 18:57:45 +0000128 self.data[key] = KeyedRef(default, self._remove, key)
Fred Drake41deb1e2001-02-01 05:27:45 +0000129 return default
130 else:
Fred Drakebd7f8182001-04-19 16:26:06 +0000131 return wr()
Fred Drake41deb1e2001-02-01 05:27:45 +0000132
Raymond Hettinger31017ae2004-03-04 08:25:44 +0000133 def update(self, dict=None, **kwargs):
Fred Drake41deb1e2001-02-01 05:27:45 +0000134 d = self.data
Raymond Hettinger31017ae2004-03-04 08:25:44 +0000135 if dict is not None:
136 if not hasattr(dict, "items"):
137 dict = type({})(dict)
138 for key, o in dict.items():
Fred Drake0a4dd392004-07-02 18:57:45 +0000139 d[key] = KeyedRef(o, self._remove, key)
Raymond Hettinger31017ae2004-03-04 08:25:44 +0000140 if len(kwargs):
141 self.update(kwargs)
Fred Drake41deb1e2001-02-01 05:27:45 +0000142
143 def values(self):
144 L = []
Fred Drakebd7f8182001-04-19 16:26:06 +0000145 for wr in self.data.values():
146 o = wr()
Fred Drake41deb1e2001-02-01 05:27:45 +0000147 if o is not None:
148 L.append(o)
149 return L
150
Fred Drake0a4dd392004-07-02 18:57:45 +0000151
152class KeyedRef(ref):
153 """Specialized reference that includes a key corresponding to the value.
154
155 This is used in the WeakValueDictionary to avoid having to create
156 a function object for each key stored in the mapping. A shared
157 callback object can use the 'key' attribute of a KeyedRef instead
158 of getting a reference to the key from an enclosing scope.
159
160 """
161
162 __slots__ = "key",
163
164 def __new__(type, ob, callback, key):
165 self = ref.__new__(type, ob, callback)
166 self.key = key
167 return self
168
169 def __init__(self, ob, callback, key):
170 super(KeyedRef, self).__init__(ob, callback)
Fred Drake746fe0f2001-09-28 19:01:26 +0000171
Fred Drake41deb1e2001-02-01 05:27:45 +0000172
Martin v. Löwis5e163332001-02-27 18:36:56 +0000173class WeakKeyDictionary(UserDict.UserDict):
Fred Drakebd7f8182001-04-19 16:26:06 +0000174 """ Mapping class that references keys weakly.
175
176 Entries in the dictionary will be discarded when there is no
177 longer a strong reference to the key. This can be used to
178 associate additional data with an object owned by other parts of
179 an application without adding attributes to those objects. This
180 can be especially useful with objects that override attribute
181 accesses.
182 """
Martin v. Löwis5e163332001-02-27 18:36:56 +0000183
184 def __init__(self, dict=None):
185 self.data = {}
Fred Drake746fe0f2001-09-28 19:01:26 +0000186 def remove(k, selfref=ref(self)):
187 self = selfref()
188 if self is not None:
189 del self.data[k]
Martin v. Löwis5e163332001-02-27 18:36:56 +0000190 self._remove = remove
Guido van Rossum009afb72002-06-10 20:00:52 +0000191 if dict is not None: self.update(dict)
Martin v. Löwis5e163332001-02-27 18:36:56 +0000192
Fred Drakeb663a2c2001-09-06 14:51:01 +0000193 def __delitem__(self, key):
Tim Peters886128f2003-05-25 01:45:11 +0000194 del self.data[ref(key)]
Fred Drakeb663a2c2001-09-06 14:51:01 +0000195
Martin v. Löwis5e163332001-02-27 18:36:56 +0000196 def __getitem__(self, key):
197 return self.data[ref(key)]
198
199 def __repr__(self):
200 return "<WeakKeyDictionary at %s>" % id(self)
201
202 def __setitem__(self, key, value):
203 self.data[ref(key, self._remove)] = value
204
205 def copy(self):
206 new = WeakKeyDictionary()
207 for key, value in self.data.items():
208 o = key()
209 if o is not None:
210 new[o] = value
Fred Drake9d2c85d2001-03-01 03:06:03 +0000211 return new
Martin v. Löwis5e163332001-02-27 18:36:56 +0000212
Fred Drake1d9e4b72001-04-16 17:34:48 +0000213 def get(self, key, default=None):
Martin v. Löwis5e163332001-02-27 18:36:56 +0000214 return self.data.get(ref(key),default)
215
Fred Drake1d9e4b72001-04-16 17:34:48 +0000216 def has_key(self, key):
Fred Drake3bae7dd2001-11-06 16:36:53 +0000217 try:
218 wr = ref(key)
219 except TypeError:
220 return 0
Raymond Hettinger54f02222002-06-01 14:18:47 +0000221 return wr in self.data
Fred Drake1d9e4b72001-04-16 17:34:48 +0000222
Raymond Hettinger54f02222002-06-01 14:18:47 +0000223 def __contains__(self, key):
224 try:
225 wr = ref(key)
226 except TypeError:
227 return 0
228 return wr in self.data
Tim Petersc411dba2002-07-16 21:35:23 +0000229
Martin v. Löwis5e163332001-02-27 18:36:56 +0000230 def items(self):
231 L = []
232 for key, value in self.data.items():
233 o = key()
234 if o is not None:
235 L.append((o, value))
236 return L
237
Fred Drake101209d2001-05-02 05:43:09 +0000238 def iteritems(self):
239 return WeakKeyedItemIterator(self)
240
241 def iterkeys(self):
242 return WeakKeyedKeyIterator(self)
243 __iter__ = iterkeys
244
245 def itervalues(self):
246 return self.data.itervalues()
247
Fred Drake1d9e4b72001-04-16 17:34:48 +0000248 def keys(self):
249 L = []
Fred Drakebd7f8182001-04-19 16:26:06 +0000250 for wr in self.data.keys():
251 o = wr()
Fred Drake1d9e4b72001-04-16 17:34:48 +0000252 if o is not None:
253 L.append(o)
254 return L
255
Martin v. Löwis5e163332001-02-27 18:36:56 +0000256 def popitem(self):
257 while 1:
258 key, value = self.data.popitem()
259 o = key()
260 if o is not None:
261 return o, value
262
Raymond Hettinger2c2d3222003-03-09 07:05:43 +0000263 def pop(self, key, *args):
264 return self.data.pop(ref(key), *args)
265
Walter Dörwald80ce6dd2004-05-27 18:16:25 +0000266 def setdefault(self, key, default=None):
Martin v. Löwis5e163332001-02-27 18:36:56 +0000267 return self.data.setdefault(ref(key, self._remove),default)
268
Raymond Hettinger31017ae2004-03-04 08:25:44 +0000269 def update(self, dict=None, **kwargs):
Martin v. Löwis5e163332001-02-27 18:36:56 +0000270 d = self.data
Raymond Hettinger31017ae2004-03-04 08:25:44 +0000271 if dict is not None:
272 if not hasattr(dict, "items"):
273 dict = type({})(dict)
274 for key, value in dict.items():
275 d[ref(key, self._remove)] = value
276 if len(kwargs):
277 self.update(kwargs)
Fred Drakebd7f8182001-04-19 16:26:06 +0000278
Martin v. Löwis5e163332001-02-27 18:36:56 +0000279
Fred Drake101209d2001-05-02 05:43:09 +0000280class BaseIter:
281 def __iter__(self):
282 return self
283
284
285class WeakKeyedKeyIterator(BaseIter):
286 def __init__(self, weakdict):
287 self._next = weakdict.data.iterkeys().next
288
289 def next(self):
290 while 1:
291 wr = self._next()
292 obj = wr()
293 if obj is not None:
294 return obj
295
296
297class WeakKeyedItemIterator(BaseIter):
298 def __init__(self, weakdict):
299 self._next = weakdict.data.iteritems().next
300
301 def next(self):
302 while 1:
303 wr, value = self._next()
304 key = wr()
305 if key is not None:
306 return key, value
307
308
309class WeakValuedValueIterator(BaseIter):
310 def __init__(self, weakdict):
311 self._next = weakdict.data.itervalues().next
312
313 def next(self):
314 while 1:
315 wr = self._next()
316 obj = wr()
317 if obj is not None:
318 return obj
319
320
321class WeakValuedItemIterator(BaseIter):
322 def __init__(self, weakdict):
Fred Drake0a4dd392004-07-02 18:57:45 +0000323 self._next = weakdict.data.itervalues().next
Fred Drake101209d2001-05-02 05:43:09 +0000324
325 def next(self):
326 while 1:
Fred Drake0a4dd392004-07-02 18:57:45 +0000327 wr = self._next()
Fred Drake101209d2001-05-02 05:43:09 +0000328 value = wr()
329 if value is not None:
Fred Drake0a4dd392004-07-02 18:57:45 +0000330 return wr.key, value