2 "Safe weakrefs", originally from pyDispatcher.
4 Provides a way to safely weakref any function, including bound methods (which
5 aren't handled by the core weakref module).
8 import weakref
, traceback
10 def safeRef(target
, onDelete
= None):
11 """Return a *safe* weak reference to a callable target
13 target -- the object to be weakly referenced, if it's a
14 bound method reference, will create a BoundMethodWeakref,
15 otherwise creates a simple weakref.
16 onDelete -- if provided, will have a hard reference stored
17 to the callable to be called after the safe reference
18 goes out of scope with the reference object, (either a
19 weakref or a BoundMethodWeakref) as argument.
21 if hasattr(target
, 'im_self'):
22 if target
.im_self
is not None:
23 # Turn a bound method into a BoundMethodWeakref instance.
24 # Keep track of these instances for lookup by disconnect().
25 assert hasattr(target
, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target
,)
26 reference
= get_bound_method_weakref(
31 if callable(onDelete
):
32 return weakref
.ref(target
, onDelete
)
34 return weakref
.ref( target
)
36 class BoundMethodWeakref(object):
37 """'Safe' and reusable weak references to instance methods
39 BoundMethodWeakref objects provide a mechanism for
40 referencing a bound method without requiring that the
41 method object itself (which is normally a transient
42 object) is kept alive. Instead, the BoundMethodWeakref
43 object keeps weak references to both the object and the
44 function which together define the instance method.
47 key -- the identity key for the reference, calculated
48 by the class's calculateKey method applied to the
49 target instance method
50 deletionMethods -- sequence of callable objects taking
51 single argument, a reference to this object which
52 will be called when *either* the target object or
53 target function is garbage collected (i.e. when
54 this object becomes invalid). These are specified
55 as the onDelete parameters of safeRef calls.
56 weakSelf -- weak reference to the target object
57 weakFunc -- weak reference to the target function
60 _allInstances -- class attribute pointing to all live
61 BoundMethodWeakref objects indexed by the class's
62 calculateKey(target) method applied to the target
63 objects. This weak value dictionary is used to
64 short-circuit creation so that multiple references
65 to the same (object, function) pair produce the
66 same BoundMethodWeakref instance.
70 _allInstances
= weakref
.WeakValueDictionary()
72 def __new__( cls
, target
, onDelete
=None, *arguments
,**named
):
73 """Create new instance or return current instance
75 Basically this method of construction allows us to
76 short-circuit creation of references to already-
77 referenced instance methods. The key corresponding
78 to the target is calculated, and if there is already
79 an existing reference, that is returned, with its
80 deletionMethods attribute updated. Otherwise the
81 new instance is created and registered in the table
82 of already-referenced methods.
84 key
= cls
.calculateKey(target
)
85 current
=cls
._allInstances
.get(key
)
86 if current
is not None:
87 current
.deletionMethods
.append( onDelete
)
90 base
= super( BoundMethodWeakref
, cls
).__new
__( cls
)
91 cls
._allInstances
[key
] = base
92 base
.__init
__( target
, onDelete
, *arguments
,**named
)
95 def __init__(self
, target
, onDelete
=None):
96 """Return a weak-reference-like instance for a bound method
98 target -- the instance-method target for the weak
99 reference, must have im_self and im_func attributes
100 and be reconstructable via:
101 target.im_func.__get__( target.im_self )
102 which is true of built-in instance methods.
103 onDelete -- optional callback which will be called
104 when this weak reference ceases to be valid
105 (i.e. either the object or the function is garbage
106 collected). Should take a single argument,
107 which will be passed a pointer to this object.
109 def remove(weak
, self
=self
):
110 """Set self.isDead to true when method or instance is destroyed"""
111 methods
= self
.deletionMethods
[:]
112 del self
.deletionMethods
[:]
114 del self
.__class
__._allInstances
[ self
.key
]
117 for function
in methods
:
119 if callable( function
):
123 traceback
.print_exc()
124 except AttributeError, err
:
125 print '''Exception during saferef %s cleanup function %s: %s'''%(
128 self
.deletionMethods
= [onDelete
]
129 self
.key
= self
.calculateKey( target
)
130 self
.weakSelf
= weakref
.ref(target
.im_self
, remove
)
131 self
.weakFunc
= weakref
.ref(target
.im_func
, remove
)
132 self
.selfName
= str(target
.im_self
)
133 self
.funcName
= str(target
.im_func
.__name
__)
135 def calculateKey( cls
, target
):
136 """Calculate the reference key for this reference
138 Currently this is a two-tuple of the id()'s of the
139 target object and the target function respectively.
141 return (id(target
.im_self
),id(target
.im_func
))
142 calculateKey
= classmethod( calculateKey
)
145 """Give a friendly representation of the object"""
146 return """%s( %s.%s )"""%(
147 self
.__class
__.__name
__,
154 def __nonzero__( self
):
155 """Whether we are still a valid reference"""
156 return self() is not None
158 def __cmp__( self
, other
):
159 """Compare with another reference"""
160 if not isinstance (other
,self
.__class
__):
161 return cmp( self
.__class
__, type(other
) )
162 return cmp( self
.key
, other
.key
)
165 """Return a strong reference to the bound method
167 If the target cannot be retrieved, then will
168 return None, otherwise returns a bound instance
169 method for our object and function.
172 You may call this method any number of times,
173 as it does not invalidate the reference.
175 target
= self
.weakSelf()
176 if target
is not None:
177 function
= self
.weakFunc()
178 if function
is not None:
179 return function
.__get
__(target
)
182 class BoundNonDescriptorMethodWeakref(BoundMethodWeakref
):
183 """A specialized BoundMethodWeakref, for platforms where instance methods
186 It assumes that the function name and the target attribute name are the
187 same, instead of assuming that the function is a descriptor. This approach
188 is equally fast, but not 100% reliable because functions can be stored on an
189 attribute named differenty than the function's name such as in:
192 def foo(self): return "foo"
195 But this shouldn't be a common use case. So, on platforms where methods
196 aren't descriptors (such as Jython) this implementation has the advantage
197 of working in the most cases.
199 def __init__(self
, target
, onDelete
=None):
200 """Return a weak-reference-like instance for a bound method
202 target -- the instance-method target for the weak
203 reference, must have im_self and im_func attributes
204 and be reconstructable via:
205 target.im_func.__get__( target.im_self )
206 which is true of built-in instance methods.
207 onDelete -- optional callback which will be called
208 when this weak reference ceases to be valid
209 (i.e. either the object or the function is garbage
210 collected). Should take a single argument,
211 which will be passed a pointer to this object.
213 assert getattr(target
.im_self
, target
.__name
__) == target
, \
214 ("method %s isn't available as the attribute %s of %s" %
215 (target
, target
.__name
__, target
.im_self
))
216 super(BoundNonDescriptorMethodWeakref
, self
).__init
__(target
, onDelete
)
219 """Return a strong reference to the bound method
221 If the target cannot be retrieved, then will
222 return None, otherwise returns a bound instance
223 method for our object and function.
226 You may call this method any number of times,
227 as it does not invalidate the reference.
229 target
= self
.weakSelf()
230 if target
is not None:
231 function
= self
.weakFunc()
232 if function
is not None:
233 # Using curry() would be another option, but it erases the
234 # "signature" of the function. That is, after a function is
235 # curried, the inspect module can't be used to determine how
236 # many arguments the function expects, nor what keyword
237 # arguments it supports, and pydispatcher needs this
239 return getattr(target
, function
.__name
__)
242 def get_bound_method_weakref(target
, onDelete
):
243 """Instantiates the appropiate BoundMethodWeakRef, depending on the details of
244 the underlying class method implementation"""
245 if hasattr(target
, '__get__'):
246 # target method is a descriptor, so the default implementation works:
247 return BoundMethodWeakref(target
=target
, onDelete
=onDelete
)
249 # no luck, use the alternative implementation:
250 return BoundNonDescriptorMethodWeakref(target
=target
, onDelete
=onDelete
)