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