diff options
author | Fred Drake <fdrake@acm.org> | 2001-02-01 05:27:45 (GMT) |
---|---|---|
committer | Fred Drake <fdrake@acm.org> | 2001-02-01 05:27:45 (GMT) |
commit | 41deb1efc2f969b58e49af669cc20d15ccdb04c6 (patch) | |
tree | 6478ef737151fb4e091594b78f611318a15d3a31 /Lib | |
parent | 2de7471d69b950a64e52a950675d59d9f4071da1 (diff) | |
download | cpython-41deb1efc2f969b58e49af669cc20d15ccdb04c6.zip cpython-41deb1efc2f969b58e49af669cc20d15ccdb04c6.tar.gz cpython-41deb1efc2f969b58e49af669cc20d15ccdb04c6.tar.bz2 |
PEP 205, Weak References -- initial checkin.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/output/test_weakref | 21 | ||||
-rw-r--r-- | Lib/test/test_weakref.py | 218 | ||||
-rw-r--r-- | Lib/weakref.py | 117 |
3 files changed, 356 insertions, 0 deletions
diff --git a/Lib/test/output/test_weakref b/Lib/test/output/test_weakref new file mode 100644 index 0000000..b3d6f97 --- /dev/null +++ b/Lib/test/output/test_weakref @@ -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 diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py new file mode 100644 index 0000000..fe19373 --- /dev/null +++ b/Lib/test/test_weakref.py @@ -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") diff --git a/Lib/weakref.py b/Lib/weakref.py new file mode 100644 index 0000000..f6e07c9 --- /dev/null +++ b/Lib/weakref.py @@ -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 |