Patch #403985: Add support for weak-keyed dictionaries
This commit is contained in:
parent
bb40dc4892
commit
5e1633365d
|
@ -3,6 +3,8 @@
|
|||
|
||||
\declaremodule{extension}{weakref}
|
||||
\moduleauthor{Fred L. Drake, Jr.}{fdrake@acm.org}
|
||||
\moduleauthor{Neil Schemenauer}{nas@arctrix.com}
|
||||
\moduleauthor{Martin von L\o"wis}{martin@loewis.home.cs.tu-berlin.de}
|
||||
\sectionauthor{Fred L. Drake, Jr.}{fdrake@acm.org}
|
||||
|
||||
\versionadded{2.1}
|
||||
|
@ -18,13 +20,6 @@ include class instances and dictionaries. Extension types can easily
|
|||
be made to support weak references; see section \ref{weakref-extension},
|
||||
``Weak References in Extension Types,'' for more information.
|
||||
|
||||
|
||||
\strong{Warning:}
|
||||
The weak dictionaries provided in the current implementation and
|
||||
described below are subject to change. They are included to solicit
|
||||
feedback and usage experience, and may be changed or removed in the
|
||||
final version.
|
||||
|
||||
\strong{Warning:}
|
||||
The desired semantics of weak-reference proxy objects are not
|
||||
completely clear; it is very difficult to create proxies which behave
|
||||
|
@ -32,9 +27,6 @@ exactly like the type of the referent. The details of these objects
|
|||
are likely to change to some degree before the final release as
|
||||
experience reports become available.
|
||||
|
||||
Please send specific feedback on this module to Fred Drake at
|
||||
\email{fdrake@acm.org}.
|
||||
|
||||
|
||||
\begin{funcdesc}{ref}{object\optional{, callback}}
|
||||
Return a weak reference to \var{object}. If \var{callback} is
|
||||
|
@ -53,15 +45,36 @@ Please send specific feedback on this module to Fred Drake at
|
|||
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
|
||||
\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}}
|
||||
|
@ -87,9 +100,16 @@ Please send specific feedback on this module to Fred Drake at
|
|||
\var{object}.
|
||||
\end{funcdesc}
|
||||
|
||||
\begin{classdesc}{WeakDictionary}{\optional{dict}}
|
||||
The class of the mapping objects returned by \function{mapping()}.
|
||||
This can be used for subclassing the implementation if needed.
|
||||
\begin{classdesc}{WeakKeyDictionary}{\optional{dict}}
|
||||
The class of the mapping objects returned by \function{mapping()}
|
||||
when \var{weakkeys} is true. This can be used for subclassing the
|
||||
implementation if needed.
|
||||
\end{classdesc}
|
||||
|
||||
\begin{classdesc}{WeakValueDictionary}{\optional{dict}}
|
||||
The class of the mapping objects returned by \function{mapping()}
|
||||
when \var{weakkeys} if false. This can be used for subclassing the
|
||||
implementation if needed.
|
||||
\end{classdesc}
|
||||
|
||||
\begin{datadesc}{ReferenceType}
|
||||
|
@ -187,8 +207,8 @@ For example, the instance type is defined with the following structure:
|
|||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyClassObject *in_class; /* The class object */
|
||||
PyObject *in_dict; /* A dictionary */
|
||||
PyObject *in_weakreflist; /* List of weak references */
|
||||
PyObject *in_dict; /* A dictionary */
|
||||
PyObject *in_weakreflist; /* List of weak references */
|
||||
} PyInstanceObject;
|
||||
\end{verbatim}
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@ Weak Valued Dictionaries
|
|||
objects are stored in weak dict
|
||||
weak dict test complete
|
||||
|
||||
Weak Keyed Dictionaries
|
||||
objects are stored in weak dict
|
||||
weak key dict test complete
|
||||
|
||||
Non-callable Proxy References
|
||||
XXX -- tests not written!
|
||||
|
||||
|
|
|
@ -152,6 +152,24 @@ for o in objects:
|
|||
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"
|
||||
print "XXX -- tests not written!"
|
||||
|
|
|
@ -20,11 +20,14 @@ from _weakref import \
|
|||
ProxyTypes = (ProxyType, CallableProxyType)
|
||||
|
||||
|
||||
def mapping(dict=None):
|
||||
return WeakDictionary(dict)
|
||||
def mapping(dict=None,weakkeys=0):
|
||||
if weakkeys:
|
||||
return WeakKeyDictionary(dict)
|
||||
else:
|
||||
return WeakValueDictionary(dict)
|
||||
|
||||
|
||||
class WeakDictionary(UserDict.UserDict):
|
||||
class WeakValueDictionary(UserDict.UserDict):
|
||||
|
||||
# We inherit the constructor without worrying about the input
|
||||
# dictionary; since it uses our .update() method, we get the right
|
||||
|
@ -112,5 +115,59 @@ class WeakDictionary(UserDict.UserDict):
|
|||
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 "<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
|
||||
|
||||
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
|
||||
|
|
|
@ -8,6 +8,7 @@ struct _PyWeakReference {
|
|||
PyObject_HEAD
|
||||
PyObject *wr_object;
|
||||
PyObject *wr_callback;
|
||||
long hash;
|
||||
PyWeakReference *wr_prev;
|
||||
PyWeakReference *wr_next;
|
||||
};
|
||||
|
@ -39,6 +40,8 @@ new_weakref(void)
|
|||
else {
|
||||
result = PyObject_NEW(PyWeakReference, &PyWeakReference_Type);
|
||||
}
|
||||
if (result)
|
||||
result->hash = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -112,6 +115,20 @@ weakref_call(PyWeakReference *self, PyObject *args, PyObject *kw)
|
|||
}
|
||||
|
||||
|
||||
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 @@ weakref_repr(PyWeakReference *self)
|
|||
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 @@ PyWeakReference_Type = {
|
|||
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*/
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue