Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | """Weak reference support for Python. |
2 | ||
3 | This module is an implementation of PEP 205: | |
4 | ||
5 | http://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 | ||
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 exceptions import ReferenceError | |
24 | ||
25 | ||
26 | ProxyTypes = (ProxyType, CallableProxyType) | |
27 | ||
28 | __all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs", | |
29 | "WeakKeyDictionary", "ReferenceType", "ProxyType", | |
30 | "CallableProxyType", "ProxyTypes", "WeakValueDictionary"] | |
31 | ||
32 | ||
33 | class 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 | def remove(wr, selfref=ref(self)): | |
47 | self = selfref() | |
48 | if self is not None: | |
49 | del self.data[wr.key] | |
50 | self._remove = remove | |
51 | UserDict.UserDict.__init__(self, *args, **kw) | |
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 | ||
174 | class 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 | ||
195 | class 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) |