weakref.py revision 61146790992e0a00b76a6bf6ecc9e53289a1ecd7
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 __init__(self, *args, **kw):
46        UserDict.UserDict.__init__(self, *args, **kw)
47        def remove(wr, selfref=ref(self)):
48            self = selfref()
49            if self is not None:
50                del self.data[wr.key]
51        self._remove = remove
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
174class 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
195class 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)
308