weakref.py revision 4fd06e0170aa75e4873b73f733bfd3f3de19d967
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     ReferenceError, \
20     CallableProxyType, \
21     ProxyType, \
22     ReferenceType
23
24ProxyTypes = (ProxyType, CallableProxyType)
25
26__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
27           "WeakKeyDictionary", "ReferenceType", "ProxyType",
28           "CallableProxyType", "ProxyTypes", "WeakValueDictionary"]
29
30
31class WeakValueDictionary(UserDict.UserDict):
32    """Mapping class that references values weakly.
33
34    Entries in the dictionary will be discarded when no strong
35    reference to the value exists anymore
36    """
37    # We inherit the constructor without worrying about the input
38    # dictionary; since it uses our .update() method, we get the right
39    # checks (if the other dictionary is a WeakValueDictionary,
40    # objects are unwrapped on the way out, and we always wrap on the
41    # way in).
42
43    def __getitem__(self, key):
44        o = self.data[key]()
45        if o is None:
46            raise KeyError, key
47        else:
48            return o
49
50    def __repr__(self):
51        return "<WeakValueDictionary at %s>" % id(self)
52
53    def __setitem__(self, key, value):
54        def remove(o, data=self.data, key=key):
55            del data[key]
56        self.data[key] = ref(value, remove)
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            def remove(o, data=self.data, key=key):
109                del data[key]
110            self.data[key] = ref(default, remove)
111            return default
112        else:
113            return wr()
114
115    def update(self, dict):
116        d = self.data
117        for key, o in dict.items():
118            def remove(o, data=d, key=key):
119                del data[key]
120            d[key] = ref(o, remove)
121
122    def values(self):
123        L = []
124        for wr in self.data.values():
125            o = wr()
126            if o is not None:
127                L.append(o)
128        return L
129
130
131class WeakKeyDictionary(UserDict.UserDict):
132    """ Mapping class that references keys weakly.
133
134    Entries in the dictionary will be discarded when there is no
135    longer a strong reference to the key. This can be used to
136    associate additional data with an object owned by other parts of
137    an application without adding attributes to those objects. This
138    can be especially useful with objects that override attribute
139    accesses.
140    """
141
142    def __init__(self, dict=None):
143        self.data = {}
144        if dict is not None: self.update(dict)
145        def remove(k, data=self.data):
146            del data[k]
147        self._remove = remove
148
149    def __getitem__(self, key):
150        return self.data[ref(key)]
151
152    def __repr__(self):
153        return "<WeakKeyDictionary at %s>" % id(self)
154
155    def __setitem__(self, key, value):
156        self.data[ref(key, self._remove)] = value
157
158    def copy(self):
159        new = WeakKeyDictionary()
160        for key, value in self.data.items():
161            o = key()
162            if o is not None:
163                new[o] = value
164        return new
165
166    def get(self, key, default=None):
167        return self.data.get(ref(key),default)
168
169    def has_key(self, key):
170        return self.data.has_key(ref(key))
171
172    def items(self):
173        L = []
174        for key, value in self.data.items():
175            o = key()
176            if o is not None:
177                L.append((o, value))
178        return L
179
180    def iteritems(self):
181        return WeakKeyedItemIterator(self)
182
183    def iterkeys(self):
184        return WeakKeyedKeyIterator(self)
185    __iter__ = iterkeys
186
187    def itervalues(self):
188        return self.data.itervalues()
189
190    def keys(self):
191        L = []
192        for wr in self.data.keys():
193            o = wr()
194            if o is not None:
195                L.append(o)
196        return L
197
198    def popitem(self):
199        while 1:
200            key, value = self.data.popitem()
201            o = key()
202            if o is not None:
203                return o, value
204
205    def setdefault(self, key, default):
206        return self.data.setdefault(ref(key, self._remove),default)
207
208    def update(self, dict):
209        d = self.data
210        for key, value in dict.items():
211            d[ref(key, self._remove)] = value
212
213
214class BaseIter:
215    def __iter__(self):
216        return self
217
218
219class WeakKeyedKeyIterator(BaseIter):
220    def __init__(self, weakdict):
221        self._next = weakdict.data.iterkeys().next
222
223    def next(self):
224        while 1:
225            wr = self._next()
226            obj = wr()
227            if obj is not None:
228                return obj
229
230
231class WeakKeyedItemIterator(BaseIter):
232    def __init__(self, weakdict):
233        self._next = weakdict.data.iteritems().next
234
235    def next(self):
236        while 1:
237            wr, value = self._next()
238            key = wr()
239            if key is not None:
240                return key, value
241
242
243class WeakValuedValueIterator(BaseIter):
244    def __init__(self, weakdict):
245        self._next = weakdict.data.itervalues().next
246
247    def next(self):
248        while 1:
249            wr = self._next()
250            obj = wr()
251            if obj is not None:
252                return obj
253
254
255class WeakValuedItemIterator(BaseIter):
256    def __init__(self, weakdict):
257        self._next = weakdict.data.iteritems().next
258
259    def next(self):
260        while 1:
261            key, wr = self._next()
262            value = wr()
263            if value is not None:
264                return key, value
265
266
267# no longer needed
268del UserDict
269