Django

Code

root/django/trunk/django/dispatch/saferef.py

Revision 8223, 10.2 kB (checked in by jacob, 4 months ago)

Major refactoring of django.dispatch with an eye towards speed. The net result is that signals are up to 90% faster.

Though some attempts and backwards-compatibility were made, speed trumped compatibility. Thus, as usual, check BackwardsIncompatibleChanges for the complete list of backwards-incompatible changes.

Thanks to Jeremy Dunck and Keith Busell for the bulk of the work; some ideas from Brian Herring's previous work (refs #4561) were incorporated.

Documentation is, sigh, still forthcoming.

Fixes #6814 and #3951 (with the new dispatch_uid argument to connect).

  • Property svn:eol-style set to native
Line 
1 """
2 "Safe weakrefs", originally from pyDispatcher.
3
4 Provides a way to safely weakref any function, including bound methods (which
5 aren't handled by the core weakref module).
6 """
7
8 import weakref, traceback
9
10 def safeRef(target, onDelete = None):
11     """Return a *safe* weak reference to a callable target
12
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
29             )
30             return reference
31     if callable(onDelete):
32         return weakref.ref(target, onDelete)
33     else:
34         return weakref.ref( target )
35
36 class BoundMethodWeakref(object):
37     """'Safe' and reusable weak references to instance methods
38
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.
45
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
58
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.
67
68     """
69    
70     _allInstances = weakref.WeakValueDictionary()
71    
72     def __new__( cls, target, onDelete=None, *arguments,**named ):
73         """Create new instance or return current instance
74
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
94    
95     def __init__(self, target, onDelete=None):
96         """Return a weak-reference-like instance for a bound method
97
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.
108         """
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
127                         )
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__)
134    
135     def calculateKey( cls, target ):
136         """Calculate the reference key for this reference
137
138         Currently this is a two-tuple of the id()'s of the
139         target object and the target function respectively.
140         """
141         return (id(target.im_self),id(target.im_func))
142     calculateKey = classmethod( calculateKey )
143    
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,
150         )
151    
152     __repr__ = __str__
153    
154     def __nonzero__( self ):
155         """Whether we are still a valid reference"""
156         return self() is not None
157    
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)
163    
164     def __call__(self):
165         """Return a strong reference to the bound method
166
167         If the target cannot be retrieved, then will
168         return None, otherwise returns a bound instance
169         method for our object and function.
170
171         Note:
172             You may call this method any number of times,
173             as it does not invalidate the reference.
174         """
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
181
182 class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
183     """A specialized BoundMethodWeakref, for platforms where instance methods
184     are not descriptors.
185
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:
190
191     class A: pass
192     def foo(self): return "foo"
193     A.bar = foo
194
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.
198     """
199     def __init__(self, target, onDelete=None):
200         """Return a weak-reference-like instance for a bound method
201
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.
212         """
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)
217
218     def __call__(self):
219         """Return a strong reference to the bound method
220
221         If the target cannot be retrieved, then will
222         return None, otherwise returns a bound instance
223         method for our object and function.
224
225         Note:
226             You may call this method any number of times,
227             as it does not invalidate the reference.
228         """
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
241
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)
Note: See TracBrowser for help on using the browser.