Kaydet (Commit) 41deb1ef authored tarafından Fred Drake's avatar Fred Drake

PEP 205, Weak References -- initial checkin.

üst 2de7471d
......@@ -24,6 +24,7 @@ typedef struct {
PyObject_HEAD
PyClassObject *in_class; /* The class object */
PyObject *in_dict; /* A dictionary */
PyObject *in_weakreflist; /* List of weak references */
} PyInstanceObject;
typedef struct {
......
......@@ -246,8 +246,8 @@ typedef struct _typeobject {
/* rich comparisons */
richcmpfunc tp_richcompare;
/* More spares */
long tp_xxx8;
/* weak reference enabler */
long tp_weaklistoffset;
#ifdef COUNT_ALLOCS
/* 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_CoerceEx(PyObject **, PyObject **);
extern DL_IMPORT(int) (*PyObject_ClearWeakRefs)(PyObject *);
/* Helpers for printing recursive container types */
extern DL_IMPORT(int) Py_ReprEnter(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_DECREF(op) \
if (--_Py_RefTotal, --(op)->ob_refcnt != 0) \
if (--_Py_RefTotal, (--((op)->ob_refcnt) != 0)) \
; \
else \
_Py_Dealloc((PyObject *)(op))
......
......@@ -160,7 +160,11 @@ extern DL_IMPORT(void) _PyObject_Del(PyObject *);
/* Macros trading binary compatibility for speed. See also pymem.h.
Note that these macros expect non-NULL object pointers.*/
#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) \
( (op)->ob_size = (size), PyObject_INIT((op), (typeobj)) )
......@@ -266,6 +270,12 @@ extern DL_IMPORT(void) _PyGC_Dump(PyGC_Head *);
#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
}
#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)
#ifdef Py_REF_DEBUG
extern long _Py_RefTotal;
#endif
if (!PyObject_ClearWeakRefs((PyObject *) inst))
return;
/* Temporarily resurrect the object. */
#ifdef Py_TRACE_REFS
#ifndef Py_REF_DEBUG
......@@ -1771,6 +1775,7 @@ PyTypeObject PyInstance_Type = {
(traverseproc)instance_traverse, /* tp_traverse */
0, /* tp_clear */
instance_richcompare, /* tp_richcompare */
offsetof(PyInstanceObject, in_weakreflist) /* tp_weaklistoffset */
};
......
......@@ -100,6 +100,10 @@ PyObject_Init(PyObject *op, PyTypeObject *tp)
/* Any changes should be reflected in PyObject_INIT (objimpl.h) */
op->ob_type = tp;
_Py_NewReference(op);
if (PyType_SUPPORTS_WEAKREFS(tp)) {
PyObject **weaklist = PyObject_GET_WEAKREFS_LISTPTR(op);
*weaklist = NULL;
}
return op;
}
......@@ -119,6 +123,10 @@ PyObject_InitVar(PyVarObject *op, PyTypeObject *tp, int size)
op->ob_size = size;
op->ob_type = tp;
_Py_NewReference((PyObject *)op);
if (PyType_SUPPORTS_WEAKREFS(tp)) {
PyObject **weaklist = PyObject_GET_WEAKREFS_LISTPTR(op);
*weaklist = NULL;
}
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,
etc. Container objects that may recursively contain themselves,
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