PEP 205, Weak References -- initial checkin.

This commit is contained in:
Fred Drake 2001-02-01 05:27:45 +00:00
parent 2de7471d69
commit 41deb1efc2
9 changed files with 1158 additions and 4 deletions

View File

@ -24,6 +24,7 @@ typedef struct {
PyObject_HEAD PyObject_HEAD
PyClassObject *in_class; /* The class object */ PyClassObject *in_class; /* The class object */
PyObject *in_dict; /* A dictionary */ PyObject *in_dict; /* A dictionary */
PyObject *in_weakreflist; /* List of weak references */
} PyInstanceObject; } PyInstanceObject;
typedef struct { typedef struct {

View File

@ -246,8 +246,8 @@ typedef struct _typeobject {
/* rich comparisons */ /* rich comparisons */
richcmpfunc tp_richcompare; richcmpfunc tp_richcompare;
/* More spares */ /* weak reference enabler */
long tp_xxx8; long tp_weaklistoffset;
#ifdef COUNT_ALLOCS #ifdef COUNT_ALLOCS
/* these must be last */ /* these must be last */
@ -284,6 +284,8 @@ extern DL_IMPORT(int) PyCallable_Check(PyObject *);
extern DL_IMPORT(int) PyNumber_Coerce(PyObject **, PyObject **); extern DL_IMPORT(int) PyNumber_Coerce(PyObject **, PyObject **);
extern DL_IMPORT(int) PyNumber_CoerceEx(PyObject **, PyObject **); extern DL_IMPORT(int) PyNumber_CoerceEx(PyObject **, PyObject **);
extern DL_IMPORT(int) (*PyObject_ClearWeakRefs)(PyObject *);
/* Helpers for printing recursive container types */ /* Helpers for printing recursive container types */
extern DL_IMPORT(int) Py_ReprEnter(PyObject *); extern DL_IMPORT(int) Py_ReprEnter(PyObject *);
extern DL_IMPORT(void) Py_ReprLeave(PyObject *); extern DL_IMPORT(void) Py_ReprLeave(PyObject *);
@ -418,7 +420,7 @@ extern DL_IMPORT(long) _Py_RefTotal;
#define Py_INCREF(op) (_Py_RefTotal++, (op)->ob_refcnt++) #define Py_INCREF(op) (_Py_RefTotal++, (op)->ob_refcnt++)
#define Py_DECREF(op) \ #define Py_DECREF(op) \
if (--_Py_RefTotal, --(op)->ob_refcnt != 0) \ if (--_Py_RefTotal, (--((op)->ob_refcnt) != 0)) \
; \ ; \
else \ else \
_Py_Dealloc((PyObject *)(op)) _Py_Dealloc((PyObject *)(op))

View File

@ -160,7 +160,11 @@ extern DL_IMPORT(void) _PyObject_Del(PyObject *);
/* Macros trading binary compatibility for speed. See also pymem.h. /* Macros trading binary compatibility for speed. See also pymem.h.
Note that these macros expect non-NULL object pointers.*/ Note that these macros expect non-NULL object pointers.*/
#define PyObject_INIT(op, typeobj) \ #define PyObject_INIT(op, typeobj) \
( (op)->ob_type = (typeobj), _Py_NewReference((PyObject *)(op)), (op) ) ((op)->ob_type = (typeobj), _Py_NewReference((PyObject *)(op)), \
(PyType_SUPPORTS_WEAKREFS((typeobj)) \
? *(PyObject_GET_WEAKREFS_LISTPTR(op)) = NULL \
: NULL), \
(op))
#define PyObject_INIT_VAR(op, typeobj, size) \ #define PyObject_INIT_VAR(op, typeobj, size) \
( (op)->ob_size = (size), PyObject_INIT((op), (typeobj)) ) ( (op)->ob_size = (size), PyObject_INIT((op), (typeobj)) )
@ -266,6 +270,12 @@ extern DL_IMPORT(void) _PyGC_Dump(PyGC_Head *);
#endif /* WITH_CYCLE_GC */ #endif /* WITH_CYCLE_GC */
/* Test if a type supports weak references */
#define PyType_SUPPORTS_WEAKREFS(t) ((t)->tp_weaklistoffset > 0)
#define PyObject_GET_WEAKREFS_LISTPTR(o) \
((PyObject **) (((char *) (o)) + (o)->ob_type->tp_weaklistoffset))
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -0,0 +1,21 @@
test_weakref
Basic Weak References
-- Liveness and referent identity
-- Reference objects with callbacks
-- Proxy objects with callbacks
-- Re-use of weak reference objects
reference objects
proxy objects
clearing ref 2
clearing ref 1
clearing ref 2
clearing ref 1
Weak Valued Dictionaries
objects are stored in weak dict
weak dict test complete
Non-callable Proxy References
XXX -- tests not written!
Callable Proxy References

218
Lib/test/test_weakref.py Normal file
View File

@ -0,0 +1,218 @@
import sys
import weakref
from test_support import TestFailed, verify
class C:
pass
print "Basic Weak References"
print "-- Liveness and referent identity"
o = C()
ref = weakref.ref(o)
verify(ref() is not None, "weak reference to live object should be live")
o2 = ref()
verify(ref() is not None, "weak ref should still be live")
verify(o is o2, "<ref>() should return original object if live")
del o, o2
del ref
cbcalled = 0
def callback(o):
global cbcalled
cbcalled = 1
o = C()
ref2 = weakref.ref(o, callback)
del o
verify(cbcalled,
"callback did not properly set 'cbcalled'")
verify(ref2() is None,
"ref2 should be dead after deleting object reference")
del ref2
print "-- Reference objects with callbacks"
o = C()
o.bar = 1
ref1 = weakref.ref(o, id)
ref2 = weakref.ref(o, id)
del o
verify(ref1() is None,
"expected reference to be invalidated")
verify(ref2() is None,
"expected reference to be invalidated")
print "-- Proxy objects with callbacks"
o = C()
o.bar = 1
ref1 = weakref.proxy(o, id)
ref2 = weakref.proxy(o, id)
del o
try:
ref1.bar
except weakref.ReferenceError:
pass
else:
raise TestFailed("expected ReferenceError exception")
try:
ref2.bar
except weakref.ReferenceError:
pass
else:
raise TestFailed("expected ReferenceError exception")
print "-- Re-use of weak reference objects"
print " reference objects"
o = C()
ref1 = weakref.ref(o)
# create a proxy to make sure that there's an intervening creation
# between these two; it should make no difference
proxy = weakref.proxy(o)
ref2 = weakref.ref(o)
verify(ref1 is ref2,
"reference object w/out callback should have been re-used")
o = C()
proxy = weakref.proxy(o)
ref1 = weakref.ref(o)
ref2 = weakref.ref(o)
verify(ref1 is ref2,
"reference object w/out callback should have been re-used")
verify(weakref.getweakrefcount(o) == 2,
"wrong weak ref count for object")
del proxy
verify(weakref.getweakrefcount(o) == 1,
"wrong weak ref count for object after deleting proxy")
print " proxy objects"
o = C()
ref3 = weakref.proxy(o)
ref4 = weakref.proxy(o)
verify(ref3 is ref4,
"proxy object w/out callback should have been re-used")
def clearing1(r):
print "clearing ref 1"
def clearing2(r):
print "clearing ref 2"
o = C()
ref1 = weakref.ref(o, clearing1)
ref2 = weakref.ref(o, clearing2)
verify(weakref.getweakrefcount(o) == 2,
"got wrong number of weak reference objects")
del o
o = C()
ref1 = weakref.ref(o, clearing1)
ref2 = weakref.ref(o, clearing2)
del ref1
verify(weakref.getweakrefs(o) == [ref2],
"list of refs does not match")
del o
o = C()
ref1 = weakref.ref(o, clearing1)
ref2 = weakref.ref(o, clearing2)
del ref2
verify(weakref.getweakrefs(o) == [ref1],
"list of refs does not match")
del o
print
print "Weak Valued Dictionaries"
class Object:
def __init__(self, arg):
self.arg = arg
def __repr__(self):
return "<Object %r>" % self.arg
dict = weakref.mapping()
objects = map(Object, range(10))
for o in objects:
dict[o.arg] = o
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 is dict[o.arg],
"wrong object returned by weak dict!")
dict.clear()
print "weak dict test complete"
print
print "Non-callable Proxy References"
print "XXX -- tests not written!"
def test_proxy(o, proxy):
o.foo = 1
verify(proxy.foo == 1,
"proxy does not reflect attribute addition")
o.foo = 2
verify(proxy.foo == 2,
"proxy does not reflect attribute modification")
del o.foo
verify(not hasattr(proxy, 'foo'),
"proxy does not reflect attribute removal")
proxy.foo = 1
verify(o.foo == 1,
"object does not reflect attribute addition via proxy")
proxy.foo = 2
verify(o.foo == 2,
"object does not reflect attribute modification via proxy")
del proxy.foo
verify(not hasattr(o, 'foo'),
"object does not reflect attribute removal via proxy")
o = C()
test_proxy(o, weakref.proxy(o))
print
print "Callable Proxy References"
class Callable:
bar = None
def __call__(self, x):
self.bar = x
o = Callable()
ref1 = weakref.proxy(o)
test_proxy(o, ref1)
verify(type(ref1) is weakref.CallableProxyType,
"proxy is not of callable type")
ref1('twinkies!')
verify(o.bar == 'twinkies!',
"call through proxy not passed through to original")
try:
ref1()
except TypeError:
# expect due to too few args
pass
else:
raise TestFailed("did not catch expected TypeError -- too few args")
try:
ref1(1, 2, 3)
except TypeError:
# expect due to too many args
pass
else:
raise TestFailed("did not catch expected TypeError -- too many args")

117
Lib/weakref.py Normal file
View File

@ -0,0 +1,117 @@
"""Weak reference support for Python.
This module is an implementation of PEP 205:
http://python.sourceforge.net/peps/pep-0205.html
"""
import UserDict
from _weakref import \
getweakrefcount, \
getweakrefs, \
ref, \
proxy, \
ReferenceError, \
CallableProxyType, \
ProxyType, \
ReferenceType
ProxyTypes = (ProxyType, CallableProxyType)
def mapping(dict=None):
return WeakDictionary(dict)
class WeakDictionary(UserDict.UserDict):
# We inherit the constructor without worrying about the input
# dictionary; since it uses our .update() method, we get the right
# checks (if the other dictionary is a WeakDictionary, objects are
# unwrapped on the way out, and we always wrap on the way in).
def __getitem__(self, key):
o = self.data.get(key)()
if o is None:
raise KeyError, key
else:
return o
def __repr__(self):
return "<WeakDictionary at %s>" % id(self)
def __setitem__(self, key, value):
def remove(o, data=self.data, key=key):
del data[key]
self.data[key] = ref(value, remove)
def copy(self):
new = WeakDictionary()
for key, ref in self.data.items():
o = ref()
if o is not None:
new[key] = o
def get(self, key, default):
try:
ref = self.data[key]
except KeyError:
return default
else:
o = ref()
if o is None:
# This should only happen
return default
else:
return o
def items(self):
L = self.data.items()
for i in range(len(L)):
key, ref = L[i]
o = ref()
if o is not None:
L[i] = key, o
return L
def popitem(self):
while 1:
key, ref = self.data.popitem()
o = ref()
if o is not None:
return key, o
def setdefault(self, key, default):
try:
ref = self.data[key]
except KeyError:
def remove(o, data=self.data, key=key):
del data[key]
ref = ref(default, remove)
self.data[key] = ref
return default
else:
return ref()
def update(self, dict):
d = self.data
L = []
for key, o in dict.items():
def remove(o, data=d, key=key):
del data[key]
L.append(key, ref(o, remove))
for key, r in L:
d[key] = r
def values(self):
L = []
for ref in self.data.values():
o = ref()
if o is not None:
L.append(o)
return L
# no longer needed
del UserDict

757
Modules/_weakref.c Normal file
View File

@ -0,0 +1,757 @@
#include "Python.h"
#include "structmember.h"
typedef struct _PyWeakReference PyWeakReference;
struct _PyWeakReference {
PyObject_HEAD
PyObject *wr_object;
PyObject *wr_callback;
PyWeakReference *wr_prev;
PyWeakReference *wr_next;
};
#define GET_WEAKREFS_LISTPTR(o) \
((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o))
static PyObject *
ReferenceError;
static PyWeakReference *
free_list = NULL;
staticforward PyTypeObject
PyWeakReference_Type;
static PyWeakReference *
new_weakref(void)
{
PyWeakReference *result;
if (free_list != NULL) {
result = free_list;
free_list = result->wr_next;
result->ob_type = &PyWeakReference_Type;
_Py_NewReference(result);
}
else {
result = PyObject_NEW(PyWeakReference, &PyWeakReference_Type);
}
return result;
}
/* This function clears the passed-in reference and removes it from the
* list of weak references for the referent. This is the only code that
* removes an item from the doubly-linked list of weak references for an
* object; it is also responsible for clearing the callback slot.
*/
static void
clear_weakref(PyWeakReference *self)
{
PyObject *callback = self->wr_callback;
if (self->wr_object != Py_None) {
PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
if (*list == self)
*list = self->wr_next;
self->wr_object = Py_None;
self->wr_callback = NULL;
if (self->wr_prev != NULL)
self->wr_prev->wr_next = self->wr_next;
if (self->wr_next != NULL)
self->wr_next->wr_prev = self->wr_prev;
self->wr_prev = NULL;
self->wr_next = NULL;
Py_XDECREF(callback);
}
}
static void
weakref_dealloc(PyWeakReference *self)
{
clear_weakref(self);
PyObject_GC_Fini((PyObject *)self);
self->wr_next = free_list;
free_list = self;
}
static int
gc_traverse(PyWeakReference *self, visitproc visit, void *arg)
{
if (self->wr_callback != NULL)
return visit(self->wr_callback, arg);
return 0;
}
static int
gc_clear(PyWeakReference *self)
{
clear_weakref(self);
return 0;
}
static PyObject *
weakref_call(PyWeakReference *self, PyObject *args, PyObject *kw)
{
static char *argnames[] = {NULL};
if (PyArg_ParseTupleAndKeywords(args, kw, ":__call__", argnames)) {
PyObject *object = self->wr_object;
Py_INCREF(object);
return (object);
}
return NULL;
}
static PyObject *
weakref_repr(PyWeakReference *self)
{
char buffer[256];
if (self->wr_object == Py_None) {
sprintf(buffer, "<weakref at %lx; dead>",
(long)(self));
}
else {
sprintf(buffer, "<weakref at %#lx; to '%s' at %#lx>",
(long)(self), self->wr_object->ob_type->tp_name,
(long)(self->wr_object));
}
return PyString_FromString(buffer);
}
statichere PyTypeObject
PyWeakReference_Type = {
PyObject_HEAD_INIT(NULL)
0,
"weakref",
sizeof(PyWeakReference) + PyGC_HEAD_SIZE,
0,
(destructor)weakref_dealloc,/*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)weakref_repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*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,
0, /*tp_doc*/
(traverseproc)gc_traverse, /*tp_traverse*/
(inquiry)gc_clear, /*tp_clear*/
};
static int
proxy_checkref(PyWeakReference *proxy)
{
if (proxy->wr_object == Py_None) {
PyErr_SetString(ReferenceError,
"weakly-referenced object no longer exists");
return 0;
}
return 1;
}
#define WRAP_UNARY(method, generic) \
static PyObject * \
method(PyWeakReference *proxy) { \
if (!proxy_checkref(proxy)) { \
return NULL; \
} \
return generic(proxy->wr_object); \
}
#define WRAP_BINARY(method, generic) \
static PyObject * \
method(PyWeakReference *proxy, PyObject *v) { \
if (!proxy_checkref(proxy)) { \
return NULL; \
} \
return generic(proxy->wr_object, v); \
}
#define WRAP_TERNARY(method, generic) \
static PyObject * \
method(PyWeakReference *proxy, PyObject *v, PyObject *w) { \
if (!proxy_checkref(proxy)) { \
return NULL; \
} \
return generic(proxy->wr_object, v, w); \
}
/* direct slots */
WRAP_BINARY(proxy_getattr, PyObject_GetAttr)
WRAP_UNARY(proxy_str, PyObject_Str)
WRAP_TERNARY(proxy_call, PyEval_CallObjectWithKeywords)
static int
proxy_print(PyWeakReference *proxy, FILE *fp, int flags)
{
if (!proxy_checkref(proxy))
return -1;
return PyObject_Print(proxy->wr_object, fp, flags);
}
static PyObject *
proxy_repr(PyWeakReference *proxy)
{
char buf[160];
sprintf(buf, "<weakref at %p to %.100s at %p>", proxy,
proxy->wr_object->ob_type->tp_name, proxy->wr_object);
return PyString_FromString(buf);
}
static int
proxy_setattr(PyWeakReference *proxy, PyObject *name, PyObject *value)
{
if (!proxy_checkref(proxy))
return -1;
return PyObject_SetAttr(proxy->wr_object, name, value);
}
static int
proxy_compare(PyWeakReference *proxy, PyObject *v)
{
if (!proxy_checkref(proxy))
return -1;
return PyObject_Compare(proxy->wr_object, v);
}
/* number slots */
WRAP_BINARY(proxy_add, PyNumber_Add)
WRAP_BINARY(proxy_sub, PyNumber_Subtract)
WRAP_BINARY(proxy_mul, PyNumber_Multiply)
WRAP_BINARY(proxy_div, PyNumber_Divide)
WRAP_BINARY(proxy_mod, PyNumber_Remainder)
WRAP_BINARY(proxy_divmod, PyNumber_Divmod)
WRAP_TERNARY(proxy_pow, PyNumber_Power)
WRAP_UNARY(proxy_neg, PyNumber_Negative)
WRAP_UNARY(proxy_pos, PyNumber_Positive)
WRAP_UNARY(proxy_abs, PyNumber_Absolute)
WRAP_UNARY(proxy_invert, PyNumber_Invert)
WRAP_BINARY(proxy_lshift, PyNumber_Lshift)
WRAP_BINARY(proxy_rshift, PyNumber_Rshift)
WRAP_BINARY(proxy_and, PyNumber_And)
WRAP_BINARY(proxy_xor, PyNumber_Xor)
WRAP_BINARY(proxy_or, PyNumber_Or)
WRAP_UNARY(proxy_int, PyNumber_Int)
WRAP_UNARY(proxy_long, PyNumber_Long)
WRAP_UNARY(proxy_float, PyNumber_Float)
WRAP_BINARY(proxy_iadd, PyNumber_InPlaceAdd)
WRAP_BINARY(proxy_isub, PyNumber_InPlaceSubtract)
WRAP_BINARY(proxy_imul, PyNumber_InPlaceMultiply)
WRAP_BINARY(proxy_idiv, PyNumber_InPlaceDivide)
WRAP_BINARY(proxy_imod, PyNumber_InPlaceRemainder)
WRAP_TERNARY(proxy_ipow, PyNumber_InPlacePower)
WRAP_BINARY(proxy_ilshift, PyNumber_InPlaceLshift)
WRAP_BINARY(proxy_irshift, PyNumber_InPlaceRshift)
WRAP_BINARY(proxy_iand, PyNumber_InPlaceAnd)
WRAP_BINARY(proxy_ixor, PyNumber_InPlaceXor)
WRAP_BINARY(proxy_ior, PyNumber_InPlaceOr)
static int
proxy_nonzero(PyWeakReference *proxy)
{
PyObject *o = proxy->wr_object;
if (!proxy_checkref(proxy))
return 1;
if (o->ob_type->tp_as_number &&
o->ob_type->tp_as_number->nb_nonzero)
return (*o->ob_type->tp_as_number->nb_nonzero)(o);
else
return 1;
}
/* sequence slots */
static PyObject *
proxy_slice(PyWeakReference *proxy, int i, int j)
{
if (!proxy_checkref(proxy))
return NULL;
return PySequence_GetSlice(proxy->wr_object, i, j);
}
static int
proxy_ass_slice(PyWeakReference *proxy, int i, int j, PyObject *value)
{
if (!proxy_checkref(proxy))
return -1;
return PySequence_SetSlice(proxy->wr_object, i, j, value);
}
static int
proxy_contains(PyWeakReference *proxy, PyObject *value)
{
if (!proxy_checkref(proxy))
return -1;
return PySequence_Contains(proxy->wr_object, value);
}
/* mapping slots */
static int
proxy_length(PyWeakReference *proxy)
{
if (!proxy_checkref(proxy))
return -1;
return PyObject_Length(proxy->wr_object);
}
WRAP_BINARY(proxy_getitem, PyObject_GetItem)
static int
proxy_setitem(PyWeakReference *proxy, PyObject *key, PyObject *value)
{
if (!proxy_checkref(proxy))
return -1;
return PyObject_SetItem(proxy->wr_object, key, value);
}
static PyNumberMethods proxy_as_number = {
(binaryfunc)proxy_add, /*nb_add*/
(binaryfunc)proxy_sub, /*nb_subtract*/
(binaryfunc)proxy_mul, /*nb_multiply*/
(binaryfunc)proxy_div, /*nb_divide*/
(binaryfunc)proxy_mod, /*nb_remainder*/
(binaryfunc)proxy_divmod, /*nb_divmod*/
(ternaryfunc)proxy_pow, /*nb_power*/
(unaryfunc)proxy_neg, /*nb_negative*/
(unaryfunc)proxy_pos, /*nb_positive*/
(unaryfunc)proxy_abs, /*nb_absolute*/
(inquiry)proxy_nonzero, /*nb_nonzero*/
(unaryfunc)proxy_invert, /*nb_invert*/
(binaryfunc)proxy_lshift, /*nb_lshift*/
(binaryfunc)proxy_rshift, /*nb_rshift*/
(binaryfunc)proxy_and, /*nb_and*/
(binaryfunc)proxy_xor, /*nb_xor*/
(binaryfunc)proxy_or, /*nb_or*/
(coercion)0, /*nb_coerce*/
(unaryfunc)proxy_int, /*nb_int*/
(unaryfunc)proxy_long, /*nb_long*/
(unaryfunc)proxy_float, /*nb_float*/
(unaryfunc)0, /*nb_oct*/
(unaryfunc)0, /*nb_hex*/
(binaryfunc)proxy_iadd, /*nb_inplace_add*/
(binaryfunc)proxy_isub, /*nb_inplace_subtract*/
(binaryfunc)proxy_imul, /*nb_inplace_multiply*/
(binaryfunc)proxy_idiv, /*nb_inplace_divide*/
(binaryfunc)proxy_imod, /*nb_inplace_remainder*/
(ternaryfunc)proxy_ipow, /*nb_inplace_power*/
(binaryfunc)proxy_ilshift, /*nb_inplace_lshift*/
(binaryfunc)proxy_irshift, /*nb_inplace_rshift*/
(binaryfunc)proxy_iand, /*nb_inplace_and*/
(binaryfunc)proxy_ixor, /*nb_inplace_xor*/
(binaryfunc)proxy_ior, /*nb_inplace_or*/
};
static PySequenceMethods proxy_as_sequence = {
(inquiry)proxy_length, /*sq_length*/
0, /*sq_concat*/
0, /*sq_repeat*/
0, /*sq_item*/
(intintargfunc)proxy_slice, /*sq_slice*/
0, /*sq_ass_item*/
(intintobjargproc)proxy_ass_slice, /*sq_ass_slice*/
(objobjproc)proxy_contains, /* sq_contains */
};
static PyMappingMethods proxy_as_mapping = {
(inquiry)proxy_length, /*mp_length*/
(binaryfunc)proxy_getitem, /*mp_subscript*/
(objobjargproc)proxy_setitem, /*mp_ass_subscript*/
};
static PyTypeObject
PyWeakProxy_Type = {
PyObject_HEAD_INIT(NULL)
0,
"weakproxy",
sizeof(PyWeakReference) + PyGC_HEAD_SIZE,
0,
/* methods */
(destructor)weakref_dealloc,/*tp_dealloc*/
(printfunc)proxy_print, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
(cmpfunc)proxy_compare, /*tp_compare*/
(unaryfunc)proxy_repr, /*tp_repr*/
&proxy_as_number, /*tp_as_number*/
&proxy_as_sequence, /*tp_as_sequence*/
&proxy_as_mapping, /*tp_as_mapping*/
0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(unaryfunc)proxy_str, /*tp_str*/
(getattrofunc)proxy_getattr,/*tp_getattro*/
(setattrofunc)proxy_setattr,/*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC
|Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
0, /*tp_doc*/
(traverseproc)gc_traverse, /*tp_traverse*/
(inquiry)gc_clear, /*tp_clear*/
};
static PyTypeObject
PyWeakCallableProxy_Type = {
PyObject_HEAD_INIT(NULL)
0,
"weakcallableproxy",
sizeof(PyWeakReference) + PyGC_HEAD_SIZE,
0,
/* methods */
(destructor)weakref_dealloc,/*tp_dealloc*/
(printfunc)proxy_print, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
(cmpfunc)proxy_compare, /*tp_compare*/
(unaryfunc)proxy_repr, /*tp_repr*/
&proxy_as_number, /*tp_as_number*/
&proxy_as_sequence, /*tp_as_sequence*/
&proxy_as_mapping, /*tp_as_mapping*/
0, /*tp_hash*/
(ternaryfunc)proxy_call, /*tp_call*/
(unaryfunc)proxy_str, /*tp_str*/
(getattrofunc)proxy_getattr,/*tp_getattro*/
(setattrofunc)proxy_setattr,/*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC
|Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
0, /*tp_doc*/
(traverseproc)gc_traverse, /*tp_traverse*/
(inquiry)gc_clear, /*tp_clear*/
};
static long
getweakrefcount(PyWeakReference *head)
{
long count = 0;
while (head != NULL) {
++count;
head = head->wr_next;
}
return count;
}
static PyObject *
weakref_getweakrefcount(PyObject *self, PyObject *args)
{
PyObject *result = NULL;
PyObject *object;
if (PyArg_ParseTuple(args, "O:getweakrefcount", &object)) {
if (PyType_SUPPORTS_WEAKREFS(object->ob_type)) {
PyWeakReference **list = GET_WEAKREFS_LISTPTR(object);
result = PyInt_FromLong(getweakrefcount(*list));
}
else
result = PyInt_FromLong(0);
}
return result;
}
static PyObject *
weakref_getweakrefs(PyObject *self, PyObject *args)
{
PyObject *result = NULL;
PyObject *object;
if (PyArg_ParseTuple(args, "O:getweakrefs", &object)) {
if (PyType_SUPPORTS_WEAKREFS(object->ob_type)) {
PyWeakReference **list = GET_WEAKREFS_LISTPTR(object);
long count = getweakrefcount(*list);
result = PyList_New(count);
if (result != NULL) {
PyWeakReference *current = *list;
long i;
for (i = 0; i < count; ++i) {
PyList_SET_ITEM(result, i, (PyObject *) current);
Py_INCREF(current);
current = current->wr_next;
}
}
}
else {
result = PyList_New(0);
}
}
return result;
}
/* Given the head of an object's list of weak references, extract the
* two callback-less refs (ref and proxy). Used to determine if the
* shared references exist and to determine the back link for newly
* inserted references.
*/
static void
get_basic_refs(PyWeakReference *head,
PyWeakReference **refp, PyWeakReference **proxyp)
{
*refp = NULL;
*proxyp = NULL;
if (head != NULL && head->wr_callback == NULL) {
if (head->ob_type == &PyWeakReference_Type) {
*refp = head;
head = head->wr_next;
}
if (head != NULL && head->wr_callback == NULL) {
*proxyp = head;
head = head->wr_next;
}
}
}
/* Insert 'newref' in the list after 'prev'. Both must be non-NULL. */
static void
insert_after(PyWeakReference *newref, PyWeakReference *prev)
{
newref->wr_prev = prev;
newref->wr_next = prev->wr_next;
if (prev->wr_next != NULL)
prev->wr_next->wr_prev = newref;
prev->wr_next = newref;
}
/* Insert 'newref' at the head of the list; 'list' points to the variable
* that stores the head.
*/
static void
insert_head(PyWeakReference *newref, PyWeakReference **list)
{
PyWeakReference *next = *list;
newref->wr_prev = NULL;
newref->wr_next = next;
if (next != NULL)
next->wr_prev = newref;
*list = newref;
}
static PyObject *
weakref_ref(PyObject *self, PyObject *args)
{
PyObject *object;
PyObject *callback = NULL;
PyWeakReference *result = NULL;
if (PyArg_ParseTuple(args, "O|O:new", &object, &callback)) {
PyWeakReference **list;
PyWeakReference *ref, *proxy;
if (!PyType_SUPPORTS_WEAKREFS(object->ob_type)) {
PyErr_Format(PyExc_TypeError,
"'%s' objects are not weakly referencable",
object->ob_type->tp_name);
return NULL;
}
list = GET_WEAKREFS_LISTPTR(object);
get_basic_refs(*list, &ref, &proxy);
if (callback == NULL) {
/* return existing weak reference if it exists */
result = ref;
Py_XINCREF(result);
}
if (result == NULL) {
result = new_weakref();
if (result != NULL) {
Py_XINCREF(callback);
result->wr_callback = callback;
result->wr_object = object;
if (callback == NULL) {
insert_head(result, list);
}
else {
PyWeakReference *prev = (proxy == NULL) ? ref : proxy;
if (prev == NULL)
insert_head(result, list);
else
insert_after(result, prev);
}
PyObject_GC_Init((PyObject *) result);
}
}
}
return (PyObject *) result;
}
static PyObject *
weakref_proxy(PyObject *self, PyObject *args)
{
PyObject *object;
PyObject *callback = NULL;
PyWeakReference *result = NULL;
if (PyArg_ParseTuple(args, "O|O:new", &object, &callback)) {
PyWeakReference **list;
PyWeakReference *ref, *proxy;
if (!PyType_SUPPORTS_WEAKREFS(object->ob_type)) {
PyErr_Format(PyExc_TypeError,
"'%s' objects are not weakly referencable",
object->ob_type->tp_name);
return NULL;
}
list = GET_WEAKREFS_LISTPTR(object);
get_basic_refs(*list, &ref, &proxy);
if (callback == NULL) {
/* attempt to return an existing weak reference if it exists */
result = proxy;
Py_XINCREF(result);
}
if (result == NULL) {
result = new_weakref();
if (result != NULL) {
PyWeakReference *prev;
if (PyCallable_Check(object))
result->ob_type = &PyWeakCallableProxy_Type;
else
result->ob_type = &PyWeakProxy_Type;
result->wr_object = object;
Py_XINCREF(callback);
result->wr_callback = callback;
if (callback == NULL)
prev = ref;
else
prev = (proxy == NULL) ? ref : proxy;
if (prev == NULL)
insert_head(result, list);
else
insert_after(result, prev);
PyObject_GC_Init((PyObject *) result);
}
}
}
return (PyObject *) result;
}
/* This is the implementation of the PyObject_ClearWeakRefs() function; it
* is installed in the init_weakref() function. It is called by the
* tp_dealloc handler to clear weak references.
*
* This returns true if the object should be deallocated, and false if the
* object is resurrected and deallocation should be aborted.
*
* This iterates through the weak references for 'object' and calls callbacks
* until one resurrects the object, at which point it stops invalidating
* weak references and returns false.
*/
static int
cleanup_helper(PyObject *object)
{
PyWeakReference **list;
if (object == NULL
|| !PyType_SUPPORTS_WEAKREFS(object->ob_type)
|| object->ob_refcnt != 0) {
PyErr_BadInternalCall();
/* not sure what we should return here */
return 1;
}
list = GET_WEAKREFS_LISTPTR(object);
while (*list != NULL) {
PyWeakReference *current = *list;
PyObject *callback = current->wr_callback;
Py_XINCREF(callback);
clear_weakref(current);
if (callback != NULL) {
PyObject *cbresult;
cbresult = PyObject_CallFunction(callback, "O", current);
if (cbresult == NULL)
PyErr_WriteUnraisable(callback);
else
Py_DECREF(cbresult);
Py_DECREF(callback);
}
}
return (object->ob_refcnt > 0 ? 0 : 1);
}
static PyMethodDef
weakref_functions[] = {
{"getweakrefcount", weakref_getweakrefcount, METH_VARARGS,
"getweakrefcount(object) -- return the number of weak references\n"
"to 'object'."},
{"getweakrefs", weakref_getweakrefs, METH_VARARGS,
"getweakrefs(object) -- return a list of all weak reference objects\n"
"that point to 'object'."},
{"proxy", weakref_proxy, METH_VARARGS,
"proxy(object[, callback]) -- create a proxy object that weakly\n"
"references 'object'. 'callback', if given, is called with a\n"
"reference to 'object' when it is about to be finalized."},
{"ref", weakref_ref, METH_VARARGS,
"new(object[, callback]) -- create a weak reference to 'object';\n"
"when 'object' is finalized, 'callback' will be called and passed\n"
"a reference to 'object'."},
{NULL, NULL, 0, NULL}
};
void
init_weakref(void)
{
PyObject *m;
PyWeakReference_Type.ob_type = &PyType_Type;
PyWeakProxy_Type.ob_type = &PyType_Type;
PyWeakCallableProxy_Type.ob_type = &PyType_Type;
m = Py_InitModule3("_weakref", weakref_functions,
"Weak-reference support module.");
if (m != NULL) {
PyObject_ClearWeakRefs = cleanup_helper;
Py_INCREF(&PyWeakReference_Type);
PyModule_AddObject(m, "ReferenceType",
(PyObject *) &PyWeakReference_Type);
Py_INCREF(&PyWeakProxy_Type);
PyModule_AddObject(m, "ProxyType",
(PyObject *) &PyWeakProxy_Type);
Py_INCREF(&PyWeakCallableProxy_Type);
PyModule_AddObject(m, "CallableProxyType",
(PyObject *) &PyWeakCallableProxy_Type);
ReferenceError = PyErr_NewException("weakref.ReferenceError",
PyExc_RuntimeError, NULL);
if (ReferenceError != NULL)
PyModule_AddObject(m, "ReferenceError", ReferenceError);
}
}

View File

@ -515,6 +515,10 @@ instance_dealloc(register PyInstanceObject *inst)
#ifdef Py_REF_DEBUG #ifdef Py_REF_DEBUG
extern long _Py_RefTotal; extern long _Py_RefTotal;
#endif #endif
if (!PyObject_ClearWeakRefs((PyObject *) inst))
return;
/* Temporarily resurrect the object. */ /* Temporarily resurrect the object. */
#ifdef Py_TRACE_REFS #ifdef Py_TRACE_REFS
#ifndef Py_REF_DEBUG #ifndef Py_REF_DEBUG
@ -1771,6 +1775,7 @@ PyTypeObject PyInstance_Type = {
(traverseproc)instance_traverse, /* tp_traverse */ (traverseproc)instance_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
instance_richcompare, /* tp_richcompare */ instance_richcompare, /* tp_richcompare */
offsetof(PyInstanceObject, in_weakreflist) /* tp_weaklistoffset */
}; };

View File

@ -100,6 +100,10 @@ PyObject_Init(PyObject *op, PyTypeObject *tp)
/* Any changes should be reflected in PyObject_INIT (objimpl.h) */ /* Any changes should be reflected in PyObject_INIT (objimpl.h) */
op->ob_type = tp; op->ob_type = tp;
_Py_NewReference(op); _Py_NewReference(op);
if (PyType_SUPPORTS_WEAKREFS(tp)) {
PyObject **weaklist = PyObject_GET_WEAKREFS_LISTPTR(op);
*weaklist = NULL;
}
return op; return op;
} }
@ -119,6 +123,10 @@ PyObject_InitVar(PyVarObject *op, PyTypeObject *tp, int size)
op->ob_size = size; op->ob_size = size;
op->ob_type = tp; op->ob_type = tp;
_Py_NewReference((PyObject *)op); _Py_NewReference((PyObject *)op);
if (PyType_SUPPORTS_WEAKREFS(tp)) {
PyObject **weaklist = PyObject_GET_WEAKREFS_LISTPTR(op);
*weaklist = NULL;
}
return op; return op;
} }
@ -1458,6 +1466,21 @@ PyObject_Free(void *p)
} }
/* Hook to clear up weak references only once the _weakref module is
imported. We use a dummy implementation to simplify the code at each
call site instead of requiring a test for NULL.
*/
static int
empty_clear_weak_refs(PyObject *o)
{
return 1;
}
int (*PyObject_ClearWeakRefs)(PyObject *) = empty_clear_weak_refs;
/* These methods are used to control infinite recursion in repr, str, print, /* These methods are used to control infinite recursion in repr, str, print,
etc. Container objects that may recursively contain themselves, etc. Container objects that may recursively contain themselves,
e.g. builtin dictionaries and lists, should used Py_ReprEnter() and e.g. builtin dictionaries and lists, should used Py_ReprEnter() and