OILS / opy / _regtest / src / weakref.py View on Github | oilshell.org

458 lines, 311 significant
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)