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).
11 def safeRef(target
, onDelete
= None):
12 """Return a *safe* weak reference to a callable target
14 target -- the object to be weakly referenced, if it's a
15 bound method reference, will create a BoundMethodWeakref,
16 otherwise creates a simple weakref.
17 onDelete -- if provided, will have a hard reference stored
18 to the callable to be called after the safe reference
19 goes out of scope with the reference object, (either a
20 weakref or a BoundMethodWeakref) as argument.
22 if hasattr(target
, 'im_self'):
23 if target
.im_self
is not None:
24 # Turn a bound method into a BoundMethodWeakref instance.
25 # Keep track of these instances for lookup by disconnect().
26 assert hasattr(target
, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target
,)
27 reference
= get_bound_method_weakref(
32 if callable(onDelete
):
33 return weakref
.ref(target
, onDelete
)
35 return weakref
.ref( target
)
37 class BoundMethodWeakref(object):
38 """'Safe' and reusable weak references to instance methods
40 BoundMethodWeakref objects provide a mechanism for
41 referencing a bound method without requiring that the
42 method object itself (which is normally a transient
43 object) is kept alive. Instead, the BoundMethodWeakref
44 object keeps weak references to both the object and the
45 function which together define the instance method.
48 key -- the identity key for the reference, calculated
49 by the class's calculateKey method applied to the
50 target instance method
51 deletionMethods -- sequence of callable objects taking
52 single argument, a reference to this object which
53 will be called when *either* the target object or
54 target function is garbage collected (i.e. when
55 this object becomes invalid). These are specified
56 as the onDelete parameters of safeRef calls.
57 weakSelf -- weak reference to the target object
58 weakFunc -- weak reference to the target function
61 _allInstances -- class attribute pointing to all live
62 BoundMethodWeakref objects indexed by the class's
63 calculateKey(target) method applied to the target
64 objects. This weak value dictionary is used to
65 short-circuit creation so that multiple references
66 to the same (object, function) pair produce the
67 same BoundMethodWeakref instance.
71 _allInstances
= weakref
.WeakValueDictionary()
73 def __new__( cls
, target
, onDelete
=None, *arguments
,**named
):
74 """Create new instance or return current instance
76 Basically this method of construction allows us to
77 short-circuit creation of references to already-
78 referenced instance methods. The key corresponding
79 to the target is calculated, and if there is already
80 an existing reference, that is returned, with its
81 deletionMethods attribute updated. Otherwise the
82 new instance is created and registered in the table
83 of already-referenced methods.
85 key
= cls
.calculateKey(target
)
86 current
=cls
._allInstances
.get(key
)
87 if current
is not None:
88 current
.deletionMethods
.append( onDelete
)
91 base
= super( BoundMethodWeakref
, cls
).__new
__( cls
)
92 cls
._allInstances
[key
] = base
93 base
.__init
__( target
, onDelete
, *arguments
,**named
)
96 def __init__(self
, target
, onDelete
=None):
97 """Return a weak-reference-like instance for a bound method
99 target -- the instance-method target for the weak
100 reference, must have im_self and im_func attributes
101 and be reconstructable via:
102 target.im_func.__get__( target.im_self )
103 which is true of built-in instance methods.
104 onDelete -- optional callback which will be called
105 when this weak reference ceases to be valid
106 (i.e. either the object or the function is garbage
107 collected). Should take a single argument,
108 which will be passed a pointer to this object.
110 def remove(weak
, self
=self
):
111 """Set self.isDead to true when method or instance is destroyed"""
112 methods
= self
.deletionMethods
[:]
113 del self
.deletionMethods
[:]
115 del self
.__class
__._allInstances
[ self
.key
]
118 for function
in methods
:
120 if callable( function
):
124 traceback
.print_exc()
125 except AttributeError, err
:
126 print '''Exception during saferef %s cleanup function %s: %s'''%(
129 self
.deletionMethods
= [onDelete
]
130 self
.key
= self
.calculateKey( target
)
131 self
.weakSelf
= weakref
.ref(target
.im_self
, remove
)
132 self
.weakFunc
= weakref
.ref(target
.im_func
, remove
)
133 self
.selfName
= str(target
.im_self
)
134 self
.funcName
= str(target
.im_func
.__name
__)
136 def calculateKey( cls
, target
):
137 """Calculate the reference key for this reference
139 Currently this is a two-tuple of the id()'s of the
140 target object and the target function respectively.
142 return (id(target
.im_self
),id(target
.im_func
))
143 calculateKey
= classmethod( calculateKey
)
146 """Give a friendly representation of the object"""
147 return """%s( %s.%s )"""%(
148 self
.__class
__.__name
__,
155 def __nonzero__( self
):
156 """Whether we are still a valid reference"""
157 return self() is not None
159 def __cmp__( self
, other
):
160 """Compare with another reference"""
161 if not isinstance (other
,self
.__class
__):
162 return cmp( self
.__class
__, type(other
) )
163 return cmp( self
.key
, other
.key
)
166 """Return a strong reference to the bound method
168 If the target cannot be retrieved, then will
169 return None, otherwise returns a bound instance
170 method for our object and function.
173 You may call this method any number of times,
174 as it does not invalidate the reference.
176 target
= self
.weakSelf()
177 if target
is not None:
178 function
= self
.weakFunc()
179 if function
is not None:
180 return function
.__get
__(target
)
183 class BoundNonDescriptorMethodWeakref(BoundMethodWeakref
):
184 """A specialized BoundMethodWeakref, for platforms where instance methods
187 It assumes that the function name and the target attribute name are the
188 same, instead of assuming that the function is a descriptor. This approach
189 is equally fast, but not 100% reliable because functions can be stored on an
190 attribute named differenty than the function's name such as in:
193 def foo(self): return "foo"
196 But this shouldn't be a common use case. So, on platforms where methods
197 aren't descriptors (such as Jython) this implementation has the advantage
198 of working in the most cases.
200 def __init__(self
, target
, onDelete
=None):
201 """Return a weak-reference-like instance for a bound method
203 target -- the instance-method target for the weak
204 reference, must have im_self and im_func attributes
205 and be reconstructable via:
206 target.im_func.__get__( target.im_self )
207 which is true of built-in instance methods.
208 onDelete -- optional callback which will be called
209 when this weak reference ceases to be valid
210 (i.e. either the object or the function is garbage
211 collected). Should take a single argument,
212 which will be passed a pointer to this object.
214 assert getattr(target
.im_self
, target
.__name
__) == target
, \
215 ("method %s isn't available as the attribute %s of %s" %
216 (target
, target
.__name
__, target
.im_self
))
217 super(BoundNonDescriptorMethodWeakref
, self
).__init
__(target
, onDelete
)
220 """Return a strong reference to the bound method
222 If the target cannot be retrieved, then will
223 return None, otherwise returns a bound instance
224 method for our object and function.
227 You may call this method any number of times,
228 as it does not invalidate the reference.
230 target
= self
.weakSelf()
231 if target
is not None:
232 function
= self
.weakFunc()
233 if function
is not None:
234 # Using partial() would be another option, but it erases the
235 # "signature" of the function. That is, after a function is
236 # curried, the inspect module can't be used to determine how
237 # many arguments the function expects, nor what keyword
238 # arguments it supports, and pydispatcher needs this
240 return getattr(target
, function
.__name
__)
243 def get_bound_method_weakref(target
, onDelete
):
244 """Instantiates the appropiate BoundMethodWeakRef, depending on the details of
245 the underlying class method implementation"""
246 if hasattr(target
, '__get__'):
247 # target method is a descriptor, so the default implementation works:
248 return BoundMethodWeakref(target
=target
, onDelete
=onDelete
)
250 # no luck, use the alternative implementation:
251 return BoundNonDescriptorMethodWeakref(target
=target
, onDelete
=onDelete
)