Index: Doc/lib/libweakref.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libweakref.tex,v retrieving revision 1.2 diff -u -r1.2 libweakref.tex --- Doc/lib/libweakref.tex 2001/02/14 02:39:11 1.2 +++ Doc/lib/libweakref.tex 2001/02/24 07:39:49 @@ -53,15 +53,35 @@ error output, but cannot be propagated; they are handled in exactly the same way as exceptions raised from an object's \method{__del__()} method. + + Weak references are hashable if the \var{object} is hashable. They + will maintain their hash value even after the \var{object} was + deleted. If \function{hash} is called the first time only after the + \var{object} was deleted, the call will raise a \exception{TypeError} + + Weak references support test for equality, but not ordering. If the + \var{object} is still alive, to references are equal if the objects + are equal (regardless of the \var{callback}). If the \var{object} + has been deleted, they are equal iff they are identical. + \end{funcdesc} -\begin{funcdesc}{mapping}{\optional{dict}} +\begin{funcdesc}{mapping}{\optional{dict\optional{, weakkeys=0}}} Return a weak dictionary. If \var{dict} is given and not \code{None}, the new dictionary will contain the items contained in \var{dict}. The values from \var{dict} must be weakly referencable; if any values which would be inserted into the new mapping are not weakly referencable, \exception{TypeError} will be raised and the new mapping will be empty. + + If the \var{weakkeys} argument is not given or zero, the values in + the dictionary are weak. That means the entries in the dictionary + will be discarded when no strong reference to the value exists + anymore. + + If the \var{weakkeys} argument is nonzero, the keys in the + dictionary are weak, i.e. the entry in the dictionary is discarded + when the last strong reference to the key is discarded. \end{funcdesc} \begin{funcdesc}{proxy}{object\optional{, callback}} Index: Lib/weakref.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/weakref.py,v retrieving revision 1.3 diff -u -r1.3 weakref.py --- Lib/weakref.py 2001/02/02 19:28:35 1.3 +++ Lib/weakref.py 2001/02/24 07:39:51 @@ -20,7 +20,9 @@ ProxyTypes = (ProxyType, CallableProxyType) -def mapping(dict=None): +def mapping(dict=None,weakkeys=0): + if weakkeys: + return WeakKeyDictionary(dict) return WeakDictionary(dict) @@ -111,6 +113,59 @@ L.append(o) return L +class WeakKeyDictionary(UserDict.UserDict): + + def __init__(self, dict=None): + self.data = {} + if dict is not None: self.update(dict) + def remove(k, data=self.data): + del data[k] + self._remove = remove + + def __getitem__(self, key): + return self.data[ref(key)] + + def __repr__(self): + return "" % 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 + + def get(self, key, default): + return self.data.get(ref(key),default) + + def items(self): + L = [] + for key, value in self.data.items(): + o = key() + if o is not None: + L.append((o, value)) + return L + + 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 + L = [] + for key, value in dict.items(): + L.append(ref(key, self._remove), value) + for key, r in L: + d[key] = r # no longer needed del UserDict Index: Lib/test/test_weakref.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/test/test_weakref.py,v retrieving revision 1.1 diff -u -r1.1 test_weakref.py --- Lib/test/test_weakref.py 2001/02/01 05:25:27 1.1 +++ Lib/test/test_weakref.py 2001/02/24 07:39:52 @@ -35,7 +35,6 @@ "ref2 should be dead after deleting object reference") del ref2 - print "-- Reference objects with callbacks" o = C() o.bar = 1 @@ -151,6 +150,24 @@ "wrong object returned by weak dict!") dict.clear() print "weak dict test complete" + +print +print "Weak Keyed Dictionaries" + +dict = weakref.mapping(weakkeys=1) +objects = map(Object, range(10)) +for o in objects: + dict[o] = o.arg +print "objects are stored in weak dict" +for o in objects: + verify(weakref.getweakrefcount(o) == 1, + "wrong number of weak references to %r!" % o) + verify(o.arg is dict[o], + "wrong object returned by weak dict!") +del objects,o +verify(len(dict)==0, "deleting the keys did not clear the dictionary") +print "weak key dict test complete" + print print "Non-callable Proxy References" Index: Modules/_weakref.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Modules/_weakref.c,v retrieving revision 1.4 diff -u -r1.4 _weakref.c --- Modules/_weakref.c 2001/02/18 05:20:18 1.4 +++ Modules/_weakref.c 2001/02/24 07:39:53 @@ -8,6 +8,7 @@ PyObject_HEAD PyObject *wr_object; PyObject *wr_callback; + long hash; PyWeakReference *wr_prev; PyWeakReference *wr_next; }; @@ -39,6 +40,8 @@ else { result = PyObject_NEW(PyWeakReference, &PyWeakReference_Type); } + if (result) + result->hash = -1; return result; } @@ -112,6 +115,20 @@ } +static long +weakref_hash(PyWeakReference *self) +{ + if (self->hash != -1) + return self->hash; + if (self->wr_object == Py_None) { + PyErr_SetString(PyExc_TypeError, "weak object has gone away"); + return -1; + } + self->hash = PyObject_Hash(self->wr_object); + return self->hash; +} + + static PyObject * weakref_repr(PyWeakReference *self) { @@ -128,6 +145,25 @@ return PyString_FromString(buffer); } +/* Weak references only support equality, not ordering. Two weak references + are equal if the underlying objects are equal. If the underlying object has + gone away, they are equal if they are identical. */ + +static PyObject * +weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op) +{ + if (op != Py_EQ || self->ob_type != other->ob_type) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (self->wr_object == Py_None || other->wr_object == Py_None) { + PyObject *res = self==other ? Py_True : Py_False; + Py_INCREF(res); + return res; + } + return PyObject_RichCompare(self->wr_object, other->wr_object, op); +} + statichere PyTypeObject PyWeakReference_Type = { @@ -145,16 +181,18 @@ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ + (hashfunc)weakref_hash, /*tp_hash*/ (ternaryfunc)weakref_call, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC | Py_TPFLAGS_HAVE_RICHCOMPARE, 0, /*tp_doc*/ (traverseproc)gc_traverse, /*tp_traverse*/ (inquiry)gc_clear, /*tp_clear*/ + (richcmpfunc)weakref_richcompare, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ };