weakref.py 7.25 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 104 105
            if o is not None:
                return key, o

    def setdefault(self, key, default):
        try:
106
            wr = self.data[key]
107
        except KeyError:
108
            self.data[key] = ref(default, self.__makeremove(key))
109 110
            return default
        else:
111
            return wr()
112 113 114 115

    def update(self, dict):
        d = self.data
        for key, o in dict.items():
116
            d[key] = ref(o, self.__makeremove(key))
117 118 119

    def values(self):
        L = []
120 121
        for wr in self.data.values():
            o = wr()
122 123 124 125
            if o is not None:
                L.append(o)
        return L

126 127 128 129 130 131 132
    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

133

134
class WeakKeyDictionary(UserDict.UserDict):
135 136 137 138 139 140 141 142 143
    """ 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.
    """
144 145 146

    def __init__(self, dict=None):
        self.data = {}
147 148 149 150
        def remove(k, selfref=ref(self)):
            self = selfref()
            if self is not None:
                del self.data[k]
151
        self._remove = remove
152
        if dict is not None: self.update(dict)
153

154 155 156 157 158 159 160
    def __delitem__(self, key):
        for ref in self.data.iterkeys():
            o = ref()
            if o == key:
                del self.data[ref]
                return

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    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
176
        return new
177

178
    def get(self, key, default=None):
179 180
        return self.data.get(ref(key),default)

181
    def has_key(self, key):
182 183 184 185
        try:
            wr = ref(key)
        except TypeError:
            return 0
186
        return wr in self.data
187

188 189 190 191 192 193
    def __contains__(self, key):
        try:
            wr = ref(key)
        except TypeError:
            return 0
        return wr in self.data
Tim Peters's avatar
Tim Peters committed
194

195 196 197 198 199 200 201 202
    def items(self):
        L = []
        for key, value in self.data.items():
            o = key()
            if o is not None:
                L.append((o, value))
        return L

203 204 205 206 207 208 209 210 211 212
    def iteritems(self):
        return WeakKeyedItemIterator(self)

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

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

213 214
    def keys(self):
        L = []
215 216
        for wr in self.data.keys():
            o = wr()
217 218 219 220
            if o is not None:
                L.append(o)
        return L

221 222 223 224 225 226 227 228 229 230 231 232 233
    def popitem(self):
        while 1:
            key, value = self.data.popitem()
            o = key()
            if o is not None:
                return o, value

    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():
234 235
            d[ref(key, self._remove)] = value

236

237 238 239 240 241 242 243 244 245 246 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
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


290 291
# no longer needed
del UserDict