Fixed #8111 -- Error message typo fix. Thanks mzgur.
[django.git] / django / dispatch / saferef.py
blob8bcfd8a1406833ea101b8aab39a1da61ce908f92
1 """
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).
6 """
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.
20 """
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(
27 target=target,
28 onDelete=onDelete
30 return reference
31 if callable(onDelete):
32 return weakref.ref(target, onDelete)
33 else:
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.
46 Attributes:
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
59 Class Attributes:
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.
68 """
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.
83 """
84 key = cls.calculateKey(target)
85 current =cls._allInstances.get(key)
86 if current is not None:
87 current.deletionMethods.append( onDelete)
88 return current
89 else:
90 base = super( BoundMethodWeakref, cls).__new__( cls )
91 cls._allInstances[key] = base
92 base.__init__( target, onDelete, *arguments,**named)
93 return base
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[:]
113 try:
114 del self.__class__._allInstances[ self.key ]
115 except KeyError:
116 pass
117 for function in methods:
118 try:
119 if callable( function ):
120 function( self )
121 except Exception, e:
122 try:
123 traceback.print_exc()
124 except AttributeError, err:
125 print '''Exception during saferef %s cleanup function %s: %s'''%(
126 self, function, e
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 )
144 def __str__(self):
145 """Give a friendly representation of the object"""
146 return """%s( %s.%s )"""%(
147 self.__class__.__name__,
148 self.selfName,
149 self.funcName,
152 __repr__ = __str__
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)
164 def __call__(self):
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.
171 Note:
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)
180 return None
182 class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
183 """A specialized BoundMethodWeakref, for platforms where instance methods
184 are not descriptors.
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:
191 class A: pass
192 def foo(self): return "foo"
193 A.bar = 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)
218 def __call__(self):
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.
225 Note:
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
238 # information.
239 return getattr(target, function.__name__)
240 return None
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)
248 else:
249 # no luck, use the alternative implementation:
250 return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete)