mirror of
https://github.com/python/cpython
synced 2026-09-29 11:30:29 +03:00
PEP 205, Weak References -- initial checkin.
This commit is contained in:
parent
2de7471d69
commit
41deb1efc2
9 changed files with 1158 additions and 4 deletions
21
Lib/test/output/test_weakref
Normal file
21
Lib/test/output/test_weakref
Normal 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
218
Lib/test/test_weakref.py
Normal 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
117
Lib/weakref.py
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue