weakref.py 7.47 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 14 15 16 17 18 19 20 21 22
import UserDict

from _weakref import \
     getweakrefcount, \
     getweakrefs, \
     ref, \
     proxy, \
     CallableProxyType, \
     ProxyType, \
     ReferenceType

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

    def __getitem__(self, key):
46
        o = self.data[key]()
47 48 49 50 51 52
        if o is None:
            raise KeyError, key
        else:
            return o

    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 61
        for key, wr in self.data.items():
            o = wr()
62 63
            if o is not None:
                new[key] = o
64
        return new
65

66
    def get(self, key, default=None):
67
        try:
68
            wr = self.data[key]
69 70 71
        except KeyError:
            return default
        else:
72
            o = wr()
73 74 75 76 77 78 79
            if o is None:
                # This should only happen
                return default
            else:
                return o

    def items(self):
80
        L = []
81 82
        for key, wr in self.data.items():
            o = wr()
83
            if o is not None:
84
                L.append((key, o))
85 86
        return L

87 88 89 90 91 92 93 94 95 96
    def iteritems(self):
        return WeakValuedItemIterator(self)

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

    def itervalues(self):
        return WeakValuedValueIterator(self)

97 98
    def popitem(self):
        while 1:
99 100
            key, wr = self.data.popitem()
            o = wr()
101 102 103
            if o is not None:
                return key, o

104 105 106 107 108 109 110 111 112 113 114 115
    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

116 117
    def setdefault(self, key, default):
        try:
118
            wr = self.data[key]
119
        except KeyError:
120
            self.data[key] = ref(default, self.__makeremove(key))
121 122
            return default
        else:
123
            return wr()
124 125 126 127

    def update(self, dict):
        d = self.data
        for key, o in dict.items():
128
            d[key] = ref(o, self.__makeremove(key))
129 130 131

    def values(self):
        L = []
132 133
        for wr in self.data.values():
            o = wr()
134 135 136 137
            if o is not None:
                L.append(o)
        return L

138 139 140 141 142 143 144
    def __makeremove(self, key):
        def remove(o, selfref=ref(self), key=key):
            self = selfref()
            if self is not None:
                del self.data[key]
        return remove

145

146
class WeakKeyDictionary(UserDict.UserDict):
147 148 149 150 151 152 153 154 155
    """ 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.
    """
156 157 158

    def __init__(self, dict=None):
        self.data = {}
159 160 161 162
        def remove(k, selfref=ref(self)):
            self = selfref()
            if self is not None:
                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 170 171 172 173 174 175 176 177 178 179 180 181 182 183
    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
184
        return new
185

186
    def get(self, key, default=None):
187 188
        return self.data.get(ref(key),default)

189
    def has_key(self, key):
190 191 192 193
        try:
            wr = ref(key)
        except TypeError:
            return 0
194
        return wr in self.data
195

196 197 198 199 200 201
    def __contains__(self, key):
        try:
            wr = ref(key)
        except TypeError:
            return 0
        return wr in self.data
Tim Peters's avatar
Tim Peters committed
202

203 204 205 206 207 208 209 210
    def items(self):
        L = []
        for key, value in self.data.items():
            o = key()
            if o is not None:
                L.append((o, value))
        return L

211 212 213 214 215 216 217 218 219 220
    def iteritems(self):
        return WeakKeyedItemIterator(self)

    def iterkeys(self):
        return WeakKeyedKeyIterator(self)
    __iter__ = iterkeys

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

221 222
    def keys(self):
        L = []
223 224
        for wr in self.data.keys():
            o = wr()
225 226 227 228
            if o is not None:
                L.append(o)
        return L

229 230 231 232 233 234 235
    def popitem(self):
        while 1:
            key, value = self.data.popitem()
            o = key()
            if o is not None:
                return o, value

236 237 238
    def pop(self, key, *args):
        return self.data.pop(ref(key), *args)

239 240 241 242 243 244
    def setdefault(self, key, default):
        return self.data.setdefault(ref(key, self._remove),default)

    def update(self, dict):
        d = self.data
        for key, value in dict.items():
245 246
            d[ref(key, self._remove)] = value

247

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
class BaseIter:
    def __iter__(self):
        return self


class WeakKeyedKeyIterator(BaseIter):
    def __init__(self, weakdict):
        self._next = weakdict.data.iterkeys().next

    def next(self):
        while 1:
            wr = self._next()
            obj = wr()
            if obj is not None:
                return obj


class WeakKeyedItemIterator(BaseIter):
    def __init__(self, weakdict):
        self._next = weakdict.data.iteritems().next

    def next(self):
        while 1:
            wr, value = self._next()
            key = wr()
            if key is not None:
                return key, value


class WeakValuedValueIterator(BaseIter):
    def __init__(self, weakdict):
        self._next = weakdict.data.itervalues().next

    def next(self):
        while 1:
            wr = self._next()
            obj = wr()
            if obj is not None:
                return obj


class WeakValuedItemIterator(BaseIter):
    def __init__(self, weakdict):
        self._next = weakdict.data.iteritems().next

    def next(self):
        while 1:
            key, wr = self._next()
            value = wr()
            if value is not None:
                return key, value


301 302
# no longer needed
del UserDict