Commit 2fe71a26 authored by Fred Drake's avatar Fred Drake

PEP 205, Weak References -- initial checkin.

parent 4c4de721
...@@ -24,6 +24,7 @@ typedef struct { ...@@ -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 {
......
...@@ -246,8 +246,8 @@ typedef struct _typeobject { ...@@ -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 *); ...@@ -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; ...@@ -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))
......
...@@ -160,7 +160,11 @@ extern DL_IMPORT(void) _PyObject_Del(PyObject *); ...@@ -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 *); ...@@ -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
......
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
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")
"""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
This diff is collapsed.
...@@ -515,6 +515,10 @@ instance_dealloc(register PyInstanceObject *inst) ...@@ -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 = { ...@@ -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 */
}; };
......
...@@ -100,6 +100,10 @@ PyObject_Init(PyObject *op, PyTypeObject *tp) ...@@ -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) ...@@ -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) ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment