Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5e16333

Browse files
committed
Patch #403985: Add support for weak-keyed dictionaries
1 parent bb40dc4 commit 5e16333

5 files changed

Lines changed: 158 additions & 21 deletions

File tree

Doc/lib/libweakref.tex

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ \section{\module{weakref} ---
33

44
\declaremodule{extension}{weakref}
55
\moduleauthor{Fred L. Drake, Jr.}{[email protected]}
6+
\moduleauthor{Neil Schemenauer}{[email protected]}
7+
\moduleauthor{Martin von L\o"wis}{[email protected]}
68
\sectionauthor{Fred L. Drake, Jr.}{[email protected]}
79

810
\versionadded{2.1}
@@ -18,23 +20,13 @@ \section{\module{weakref} ---
1820
be made to support weak references; see section \ref{weakref-extension},
1921
``Weak References in Extension Types,'' for more information.
2022

21-
22-
\strong{Warning:}
23-
The weak dictionaries provided in the current implementation and
24-
described below are subject to change. They are included to solicit
25-
feedback and usage experience, and may be changed or removed in the
26-
final version.
27-
2823
\strong{Warning:}
2924
The desired semantics of weak-reference proxy objects are not
3025
completely clear; it is very difficult to create proxies which behave
3126
exactly like the type of the referent. The details of these objects
3227
are likely to change to some degree before the final release as
3328
experience reports become available.
3429

35-
Please send specific feedback on this module to Fred Drake at
36-
37-
3830

3931
\begin{funcdesc}{ref}{object\optional{, callback}}
4032
Return a weak reference to \var{object}. If \var{callback} is
@@ -53,15 +45,36 @@ \section{\module{weakref} ---
5345
error output, but cannot be propagated; they are handled in exactly
5446
the same way as exceptions raised from an object's
5547
\method{__del__()} method.
48+
49+
Weak references are hashable if the \var{object} is hashable. They
50+
will maintain their hash value even after the \var{object} was
51+
deleted. If \function{hash()} is called the first time only after
52+
the \var{object} was deleted, the call will raise
53+
\exception{TypeError}.
54+
55+
Weak references support test for equality, but not ordering. If the
56+
\var{object} is still alive, to references are equal if the objects
57+
are equal (regardless of the \var{callback}). If the \var{object}
58+
has been deleted, they are equal iff they are identical.
59+
5660
\end{funcdesc}
5761

58-
\begin{funcdesc}{mapping}{\optional{dict}}
62+
\begin{funcdesc}{mapping}{\optional{dict\optional{, weakkeys=0}}}
5963
Return a weak dictionary. If \var{dict} is given and not
6064
\code{None}, the new dictionary will contain the items contained in
6165
\var{dict}. The values from \var{dict} must be weakly referencable;
6266
if any values which would be inserted into the new mapping are not
6367
weakly referencable, \exception{TypeError} will be raised and the
6468
new mapping will be empty.
69+
70+
If the \var{weakkeys} argument is not given or zero, the values in
71+
the dictionary are weak. That means the entries in the dictionary
72+
will be discarded when no strong reference to the value exists
73+
anymore.
74+
75+
If the \var{weakkeys} argument is nonzero, the keys in the
76+
dictionary are weak, i.e. the entry in the dictionary is discarded
77+
when the last strong reference to the key is discarded.
6578
\end{funcdesc}
6679

6780
\begin{funcdesc}{proxy}{object\optional{, callback}}
@@ -87,9 +100,16 @@ \section{\module{weakref} ---
87100
\var{object}.
88101
\end{funcdesc}
89102

90-
\begin{classdesc}{WeakDictionary}{\optional{dict}}
91-
The class of the mapping objects returned by \function{mapping()}.
92-
This can be used for subclassing the implementation if needed.
103+
\begin{classdesc}{WeakKeyDictionary}{\optional{dict}}
104+
The class of the mapping objects returned by \function{mapping()}
105+
when \var{weakkeys} is true. This can be used for subclassing the
106+
implementation if needed.
107+
\end{classdesc}
108+
109+
\begin{classdesc}{WeakValueDictionary}{\optional{dict}}
110+
The class of the mapping objects returned by \function{mapping()}
111+
when \var{weakkeys} if false. This can be used for subclassing the
112+
implementation if needed.
93113
\end{classdesc}
94114

95115
\begin{datadesc}{ReferenceType}
@@ -187,8 +207,8 @@ \subsection{Weak References in Extension Types
187207
typedef struct {
188208
PyObject_HEAD
189209
PyClassObject *in_class; /* The class object */
190-
PyObject *in_dict; /* A dictionary */
191-
PyObject *in_weakreflist; /* List of weak references */
210+
PyObject *in_dict; /* A dictionary */
211+
PyObject *in_weakreflist; /* List of weak references */
192212
} PyInstanceObject;
193213
\end{verbatim}
194214

Lib/test/output/test_weakref

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Weak Valued Dictionaries
1515
objects are stored in weak dict
1616
weak dict test complete
1717

18+
Weak Keyed Dictionaries
19+
objects are stored in weak dict
20+
weak key dict test complete
21+
1822
Non-callable Proxy References
1923
XXX -- tests not written!
2024

Lib/test/test_weakref.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,24 @@ def __repr__(self):
152152
dict.clear()
153153
print "weak dict test complete"
154154

155+
print
156+
print "Weak Keyed Dictionaries"
157+
158+
dict = weakref.mapping(weakkeys=1)
159+
objects = map(Object, range(10))
160+
for o in objects:
161+
dict[o] = o.arg
162+
print "objects are stored in weak dict"
163+
for o in objects:
164+
verify(weakref.getweakrefcount(o) == 1,
165+
"wrong number of weak references to %r!" % o)
166+
verify(o.arg is dict[o],
167+
"wrong object returned by weak dict!")
168+
del objects,o
169+
verify(len(dict)==0, "deleting the keys did not clear the dictionary")
170+
print "weak key dict test complete"
171+
172+
155173
print
156174
print "Non-callable Proxy References"
157175
print "XXX -- tests not written!"

Lib/weakref.py

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020
ProxyTypes = (ProxyType, CallableProxyType)
2121

2222

23-
def mapping(dict=None):
24-
return WeakDictionary(dict)
23+
def mapping(dict=None,weakkeys=0):
24+
if weakkeys:
25+
return WeakKeyDictionary(dict)
26+
else:
27+
return WeakValueDictionary(dict)
2528

2629

27-
class WeakDictionary(UserDict.UserDict):
30+
class WeakValueDictionary(UserDict.UserDict):
2831

2932
# We inherit the constructor without worrying about the input
3033
# dictionary; since it uses our .update() method, we get the right
@@ -112,5 +115,59 @@ def values(self):
112115
return L
113116

114117

118+
class WeakKeyDictionary(UserDict.UserDict):
119+
120+
def __init__(self, dict=None):
121+
self.data = {}
122+
if dict is not None: self.update(dict)
123+
def remove(k, data=self.data):
124+
del data[k]
125+
self._remove = remove
126+
127+
def __getitem__(self, key):
128+
return self.data[ref(key)]
129+
130+
def __repr__(self):
131+
return "<WeakKeyDictionary at %s>" % id(self)
132+
133+
def __setitem__(self, key, value):
134+
self.data[ref(key, self._remove)] = value
135+
136+
def copy(self):
137+
new = WeakKeyDictionary()
138+
for key, value in self.data.items():
139+
o = key()
140+
if o is not None:
141+
new[o] = value
142+
143+
def get(self, key, default):
144+
return self.data.get(ref(key),default)
145+
146+
def items(self):
147+
L = []
148+
for key, value in self.data.items():
149+
o = key()
150+
if o is not None:
151+
L.append((o, value))
152+
return L
153+
154+
def popitem(self):
155+
while 1:
156+
key, value = self.data.popitem()
157+
o = key()
158+
if o is not None:
159+
return o, value
160+
161+
def setdefault(self, key, default):
162+
return self.data.setdefault(ref(key, self._remove),default)
163+
164+
def update(self, dict):
165+
d = self.data
166+
L = []
167+
for key, value in dict.items():
168+
L.append(ref(key, self._remove), value)
169+
for key, r in L:
170+
d[key] = r
171+
115172
# no longer needed
116173
del UserDict

Modules/_weakref.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ struct _PyWeakReference {
88
PyObject_HEAD
99
PyObject *wr_object;
1010
PyObject *wr_callback;
11+
long hash;
1112
PyWeakReference *wr_prev;
1213
PyWeakReference *wr_next;
1314
};
@@ -39,6 +40,8 @@ new_weakref(void)
3940
else {
4041
result = PyObject_NEW(PyWeakReference, &PyWeakReference_Type);
4142
}
43+
if (result)
44+
result->hash = -1;
4245
return result;
4346
}
4447

@@ -112,6 +115,20 @@ weakref_call(PyWeakReference *self, PyObject *args, PyObject *kw)
112115
}
113116

114117

118+
static long
119+
weakref_hash(PyWeakReference *self)
120+
{
121+
if (self->hash != -1)
122+
return self->hash;
123+
if (self->wr_object == Py_None) {
124+
PyErr_SetString(PyExc_TypeError, "weak object has gone away");
125+
return -1;
126+
}
127+
self->hash = PyObject_Hash(self->wr_object);
128+
return self->hash;
129+
}
130+
131+
115132
static PyObject *
116133
weakref_repr(PyWeakReference *self)
117134
{
@@ -128,6 +145,25 @@ weakref_repr(PyWeakReference *self)
128145
return PyString_FromString(buffer);
129146
}
130147

148+
/* Weak references only support equality, not ordering. Two weak references
149+
are equal if the underlying objects are equal. If the underlying object has
150+
gone away, they are equal if they are identical. */
151+
152+
static PyObject *
153+
weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
154+
{
155+
if (op != Py_EQ || self->ob_type != other->ob_type) {
156+
Py_INCREF(Py_NotImplemented);
157+
return Py_NotImplemented;
158+
}
159+
if (self->wr_object == Py_None || other->wr_object == Py_None) {
160+
PyObject *res = self==other ? Py_True : Py_False;
161+
Py_INCREF(res);
162+
return res;
163+
}
164+
return PyObject_RichCompare(self->wr_object, other->wr_object, op);
165+
}
166+
131167

132168
statichere PyTypeObject
133169
PyWeakReference_Type = {
@@ -145,16 +181,18 @@ PyWeakReference_Type = {
145181
0, /*tp_as_number*/
146182
0, /*tp_as_sequence*/
147183
0, /*tp_as_mapping*/
148-
0, /*tp_hash*/
184+
(hashfunc)weakref_hash, /*tp_hash*/
149185
(ternaryfunc)weakref_call, /*tp_call*/
150186
0, /*tp_str*/
151187
0, /*tp_getattro*/
152188
0, /*tp_setattro*/
153189
0, /*tp_as_buffer*/
154-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC,
190+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC | Py_TPFLAGS_HAVE_RICHCOMPARE,
155191
0, /*tp_doc*/
156192
(traverseproc)gc_traverse, /*tp_traverse*/
157193
(inquiry)gc_clear, /*tp_clear*/
194+
(richcmpfunc)weakref_richcompare, /*tp_richcompare*/
195+
0, /*tp_weaklistoffset*/
158196
};
159197

160198

0 commit comments

Comments
 (0)