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