App Engine Python SDK version 1.7.4 (2)
[gae.git] / python / lib / django_1_4 / django / dispatch / saferef.py
blob1c7d16474fb58038e47088f5346b01a935e8b6ad
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 traceback
9 import weakref
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.
21 """
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(
28 target=target,
29 onDelete=onDelete
31 return reference
32 if callable(onDelete):
33 return weakref.ref(target, onDelete)
34 else:
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.
47 Attributes:
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
60 Class Attributes:
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.
69 """
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.
84 """
85 key = cls.calculateKey(target)
86 current =cls._allInstances.get(key)
87 if current is not None:
88 current.deletionMethods.append( onDelete)
89 return current
90 else:
91 base = super( BoundMethodWeakref, cls).__new__( cls )
92 cls._allInstances[key] = base
93 base.__init__( target, onDelete, *arguments,**named)
94 return base
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[:]
114 try:
115 del self.__class__._allInstances[ self.key ]
116 except KeyError:
117 pass
118 for function in methods:
119 try:
120 if callable( function ):
121 function( self )
122 except Exception, e:
123 try:
124 traceback.print_exc()
125 except AttributeError, err:
126 print '''Exception during saferef %s cleanup function %s: %s'''%(
127 self, function, e
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 )
145 def __str__(self):
146 """Give a friendly representation of the object"""
147 return """%s( %s.%s )"""%(
148 self.__class__.__name__,
149 self.selfName,
150 self.funcName,
153 __repr__ = __str__
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)
165 def __call__(self):
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.
172 Note:
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)
181 return None
183 class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
184 """A specialized BoundMethodWeakref, for platforms where instance methods
185 are not descriptors.
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:
192 class A: pass
193 def foo(self): return "foo"
194 A.bar = 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)
219 def __call__(self):
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.
226 Note:
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
239 # information.
240 return getattr(target, function.__name__)
241 return None
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)
249 else:
250 # no luck, use the alternative implementation:
251 return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete)