weakref.py 9.84 KB
Newer Older
1 2 3 4 5 6 7
"""Weak reference support for Python.

This module is an implementation of PEP 205:

http://python.sourceforge.net/peps/pep-0205.html
"""

8 9 10 11
# Naming convention: Variables named "wr" are weak reference objects;
# they are called this instead of "ref" to avoid name collisions with
# the module-global ref() function imported from _weakref.

12 13
import UserDict

14 15 16 17 18 19 20 21
from _weakref import (
     getweakrefcount,
     getweakrefs,
     ref,
     proxy,
     CallableProxyType,
     ProxyType,
     ReferenceType)
22

23 24 25
from exceptions import ReferenceError


26 27
ProxyTypes = (ProxyType, CallableProxyType)

28
__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
29 30
           "WeakKeyDictionary", "ReferenceType", "ProxyType",
           "CallableProxyType", "ProxyTypes", "WeakValueDictionary"]
31

32

33
class WeakValueDictionary(UserDict.UserDict):
34
    """Mapping class that references values weakly.
35

36 37 38
    Entries in the dictionary will be discarded when no strong
    reference to the value exists anymore
    """
39 40
    # We inherit the constructor without worrying about the input
    # dictionary; since it uses our .update() method, we get the right
41 42 43
    # checks (if the other dictionary is a WeakValueDictionary,
    # objects are unwrapped on the way out, and we always wrap on the
    # way in).
44

45 46 47 48 49 50
    def __init__(self, *args, **kw):
        def remove(wr, selfref=ref(self)):
            self = selfref()
            if self is not None:
                del self.data[wr.key]
        self._remove = remove
51
        UserDict.UserDict.__init__(self, *args, **kw)
52

53
    def __getitem__(self, key):
54
        o = self.data[key]()
55 56 57 58 59
        if o is None:
            raise KeyError, key
        else:
            return o

60 61 62 63 64 65 66 67 68 69 70 71 72 73
    def __contains__(self, key):
        try:
            o = self.data[key]()
        except KeyError:
            return False
        return o is not None

    def has_key(self, key):
        try:
            o = self.data[key]()
        except KeyError:
            return False
        return o is not None

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 83
        for key, wr in self.data.items():
            o = wr()
84 85
            if o is not None:
                new[key] = o
86
        return new
87

88
    def get(self, key, default=None):
89
        try:
90
            wr = self.data[key]
91 92 93
        except KeyError:
            return default
        else:
94
            o = wr()
95 96 97 98 99 100 101
            if o is None:
                # This should only happen
                return default
            else:
                return o

    def items(self):
102
        L = []
103 104
        for key, wr in self.data.items():
            o = wr()
105
            if o is not None:
106
                L.append((key, o))
107 108
        return L

109
    def iteritems(self):
110 111 112 113
        for wr in self.data.itervalues():
            value = wr()
            if value is not None:
                yield wr.key, value
114 115 116

    def iterkeys(self):
        return self.data.iterkeys()
117 118 119

    def __iter__(self):
        return self.data.iterkeys()
120

121 122 123 124 125 126 127 128 129 130 131 132
    def itervaluerefs(self):
        """Return an iterator that yields the weak references to the values.

        The references are not guaranteed to be 'live' at the time
        they are used, so the result of calling the references needs
        to be checked before being used.  This can be used to avoid
        creating references that will cause the garbage collector to
        keep the values around longer than needed.

        """
        return self.data.itervalues()

133
    def itervalues(self):
134 135 136 137
        for wr in self.data.itervalues():
            obj = wr()
            if obj is not None:
                yield obj
138

139 140
    def popitem(self):
        while 1:
141 142
            key, wr = self.data.popitem()
            o = wr()
143 144 145
            if o is not None:
                return key, o

146 147 148 149 150 151 152 153 154 155 156 157
    def pop(self, key, *args):
        try:
            o = self.data.pop(key)()
        except KeyError:
            if args:
                return args[0]
            raise
        if o is None:
            raise KeyError, key
        else:
            return o

158
    def setdefault(self, key, default=None):
159
        try:
160
            wr = self.data[key]
161
        except KeyError:
162
            self.data[key] = KeyedRef(default, self._remove, key)
163 164
            return default
        else:
165
            return wr()
166

167
    def update(self, dict=None, **kwargs):
168
        d = self.data
169 170 171 172
        if dict is not None:
            if not hasattr(dict, "items"):
                dict = type({})(dict)
            for key, o in dict.items():
173
                d[key] = KeyedRef(o, self._remove, key)
174 175
        if len(kwargs):
            self.update(kwargs)
176

177 178 179 180 181 182 183 184 185 186 187 188
    def valuerefs(self):
        """Return a list of weak references to the values.

        The references are not guaranteed to be 'live' at the time
        they are used, so the result of calling the references needs
        to be checked before being used.  This can be used to avoid
        creating references that will cause the garbage collector to
        keep the values around longer than needed.

        """
        return self.data.values()

189 190
    def values(self):
        L = []
191 192
        for wr in self.data.values():
            o = wr()
193 194 195 196
            if o is not None:
                L.append(o)
        return L

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

class KeyedRef(ref):
    """Specialized reference that includes a key corresponding to the value.

    This is used in the WeakValueDictionary to avoid having to create
    a function object for each key stored in the mapping.  A shared
    callback object can use the 'key' attribute of a KeyedRef instead
    of getting a reference to the key from an enclosing scope.

    """

    __slots__ = "key",

    def __new__(type, ob, callback, key):
        self = ref.__new__(type, ob, callback)
        self.key = key
        return self

    def __init__(self, ob, callback, key):
        super(KeyedRef,  self).__init__(ob, callback)
217

218

219
class WeakKeyDictionary(UserDict.UserDict):
220 221 222 223 224 225 226 227 228
    """ Mapping class that references keys weakly.

    Entries in the dictionary will be discarded when there is no
    longer a strong reference to the key. This can be used to
    associate additional data with an object owned by other parts of
    an application without adding attributes to those objects. This
    can be especially useful with objects that override attribute
    accesses.
    """
229 230 231

    def __init__(self, dict=None):
        self.data = {}
232 233 234 235
        def remove(k, selfref=ref(self)):
            self = selfref()
            if self is not None:
                del self.data[k]
236
        self._remove = remove
237
        if dict is not None: self.update(dict)
238

239
    def __delitem__(self, key):
240
        del self.data[ref(key)]
241

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
    def __getitem__(self, key):
        return self.data[ref(key)]

    def __repr__(self):
        return "<WeakKeyDictionary at %s>" % id(self)

    def __setitem__(self, key, value):
        self.data[ref(key, self._remove)] = value

    def copy(self):
        new = WeakKeyDictionary()
        for key, value in self.data.items():
            o = key()
            if o is not None:
                new[o] = value
257
        return new
258

259
    def get(self, key, default=None):
260 261
        return self.data.get(ref(key),default)

262
    def has_key(self, key):
263 264 265 266
        try:
            wr = ref(key)
        except TypeError:
            return 0
267
        return wr in self.data
268

269 270 271 272 273 274
    def __contains__(self, key):
        try:
            wr = ref(key)
        except TypeError:
            return 0
        return wr in self.data
Tim Peters's avatar
Tim Peters committed
275

276 277 278 279 280 281 282 283
    def items(self):
        L = []
        for key, value in self.data.items():
            o = key()
            if o is not None:
                L.append((o, value))
        return L

284
    def iteritems(self):
285 286 287 288
        for wr, value in self.data.iteritems():
            key = wr()
            if key is not None:
                yield key, value
289

290 291 292 293 294 295 296 297 298 299 300 301
    def iterkeyrefs(self):
        """Return an iterator that yields the weak references to the keys.

        The references are not guaranteed to be 'live' at the time
        they are used, so the result of calling the references needs
        to be checked before being used.  This can be used to avoid
        creating references that will cause the garbage collector to
        keep the keys around longer than needed.

        """
        return self.data.iterkeys()

302
    def iterkeys(self):
303 304 305 306 307 308 309
        for wr in self.data.iterkeys():
            obj = wr()
            if obj is not None:
                yield obj

    def __iter__(self):
        return self.iterkeys()
310 311 312 313

    def itervalues(self):
        return self.data.itervalues()

314 315 316 317 318 319 320 321 322 323 324 325
    def keyrefs(self):
        """Return a list of weak references to the keys.

        The references are not guaranteed to be 'live' at the time
        they are used, so the result of calling the references needs
        to be checked before being used.  This can be used to avoid
        creating references that will cause the garbage collector to
        keep the keys around longer than needed.

        """
        return self.data.keys()

326 327
    def keys(self):
        L = []
328 329
        for wr in self.data.keys():
            o = wr()
330 331 332 333
            if o is not None:
                L.append(o)
        return L

334 335 336 337 338 339 340
    def popitem(self):
        while 1:
            key, value = self.data.popitem()
            o = key()
            if o is not None:
                return o, value

341 342 343
    def pop(self, key, *args):
        return self.data.pop(ref(key), *args)

344
    def setdefault(self, key, default=None):
345 346
        return self.data.setdefault(ref(key, self._remove),default)

347
    def update(self, dict=None, **kwargs):
348
        d = self.data
349 350 351 352 353 354 355
        if dict is not None:
            if not hasattr(dict, "items"):
                dict = type({})(dict)
            for key, value in dict.items():
                d[ref(key, self._remove)] = value
        if len(kwargs):
            self.update(kwargs)