Django

Code

root/django/branches/newforms-admin/django/dispatch/saferef.py

Revision 6613, 10.1 kB (checked in by jkocherhans, 1 year ago)

newforms-admin: Merged to [6612]

  • Property svn:eol-style set to native
Line 
1 """Refactored "safe reference" from dispatcher.py"""
2 import weakref, traceback
3 from django.utils.functional import curry
4
5 def safeRef(target, onDelete = None):
6     """Return a *safe* weak reference to a callable target
7
8     target -- the object to be weakly referenced, if it's a
9         bound method reference, will create a BoundMethodWeakref,
10         otherwise creates a simple weakref.
11     onDelete -- if provided, will have a hard reference stored
12         to the callable to be called after the safe reference
13         goes out of scope with the reference object, (either a
14         weakref or a BoundMethodWeakref) as argument.
15     """
16     if hasattr(target, 'im_self'):
17         if target.im_self is not None:
18             # Turn a bound method into a BoundMethodWeakref instance.
19             # Keep track of these instances for lookup by disconnect().
20             assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
21             reference = get_bound_method_weakref(
22                 target=target,
23                 onDelete=onDelete
24             )
25             return reference
26     if callable(onDelete):
27         return weakref.ref(target, onDelete)
28     else:
29         return weakref.ref( target )
30
31 class BoundMethodWeakref(object):
32     """'Safe' and reusable weak references to instance methods
33
34     BoundMethodWeakref objects provide a mechanism for
35     referencing a bound method without requiring that the
36     method object itself (which is normally a transient
37     object) is kept alive.  Instead, the BoundMethodWeakref
38     object keeps weak references to both the object and the
39     function which together define the instance method.
40
41     Attributes:
42         key -- the identity key for the reference, calculated
43             by the class's calculateKey method applied to the
44             target instance method
45         deletionMethods -- sequence of callable objects taking
46             single argument, a reference to this object which
47             will be called when *either* the target object or
48             target function is garbage collected (i.e. when
49             this object becomes invalid).  These are specified
50             as the onDelete parameters of safeRef calls.
51         weakSelf -- weak reference to the target object
52         weakFunc -- weak reference to the target function
53
54     Class Attributes:
55         _allInstances -- class attribute pointing to all live
56             BoundMethodWeakref objects indexed by the class's
57             calculateKey(target) method applied to the target
58             objects.  This weak value dictionary is used to
59             short-circuit creation so that multiple references
60             to the same (object, function) pair produce the
61             same BoundMethodWeakref instance.
62
63     """
64     _allInstances = weakref.WeakValueDictionary()
65     def __new__( cls, target, onDelete=None, *arguments,**named ):
66         """Create new instance or return current instance
67
68         Basically this method of construction allows us to
69         short-circuit creation of references to already-
70         referenced instance methods.  The key corresponding
71         to the target is calculated, and if there is already
72         an existing reference, that is returned, with its
73         deletionMethods attribute updated.  Otherwise the
74         new instance is created and registered in the table
75         of already-referenced methods.
76         """
77         key = cls.calculateKey(target)
78         current =cls._allInstances.get(key)
79         if current is not None:
80             current.deletionMethods.append( onDelete)
81             return current
82         else:
83             base = super( BoundMethodWeakref, cls).__new__( cls )
84             cls._allInstances[key] = base
85             base.__init__( target, onDelete, *arguments,**named)
86             return base
87     def __init__(self, target, onDelete=None):
88         """Return a weak-reference-like instance for a bound method
89
90         target -- the instance-method target for the weak
91             reference, must have im_self and im_func attributes
92             and be reconstructable via:
93                 target.im_func.__get__( target.im_self )
94             which is true of built-in instance methods.
95         onDelete -- optional callback which will be called
96             when this weak reference ceases to be valid
97             (i.e. either the object or the function is garbage
98             collected).  Should take a single argument,
99             which will be passed a pointer to this object.
100         """
101         def remove(weak, self=self):
102             """Set self.isDead to true when method or instance is destroyed"""
103             methods = self.deletionMethods[:]
104             del self.deletionMethods[:]
105             try:
106                 del self.__class__._allInstances[ self.key ]
107             except KeyError:
108                 pass
109             for function in methods:
110                 try:
111                     if callable( function ):
112                         function( self )
113                 except Exception, e:
114                     try:
115                         traceback.print_exc()
116                     except AttributeError, err:
117                         print '''Exception during saferef %s cleanup function %s: %s'''%(
118                             self, function, e
119                         )
120         self.deletionMethods = [onDelete]
121         self.key = self.calculateKey( target )
122         self.weakSelf = weakref.ref(target.im_self, remove)
123         self.weakFunc = weakref.ref(target.im_func, remove)
124         self.selfName = str(target.im_self)
125         self.funcName = str(target.im_func.__name__)
126     def calculateKey( cls, target ):
127         """Calculate the reference key for this reference
128
129         Currently this is a two-tuple of the id()'s of the
130         target object and the target function respectively.
131         """
132         return (id(target.im_self),id(target.im_func))
133     calculateKey = classmethod( calculateKey )
134     def __str__(self):
135         """Give a friendly representation of the object"""
136         return """%s( %s.%s )"""%(
137             self.__class__.__name__,
138             self.selfName,
139             self.funcName,
140         )
141     __repr__ = __str__
142     def __nonzero__( self ):
143         """Whether we are still a valid reference"""
144         return self() is not None
145     def __cmp__( self, other ):
146         """Compare with another reference"""
147         if not isinstance (other,self.__class__):
148             return cmp( self.__class__, type(other) )
149         return cmp( self.key, other.key)
150     def __call__(self):
151         """Return a strong reference to the bound method
152
153         If the target cannot be retrieved, then will
154         return None, otherwise returns a bound instance
155         method for our object and function.
156
157         Note:
158             You may call this method any number of times,
159             as it does not invalidate the reference.
160         """
161         target = self.weakSelf()
162         if target is not None:
163             function = self.weakFunc()
164             if function is not None:
165                 return function.__get__(target)
166         return None
167
168 class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
169     """A specialized BoundMethodWeakref, for platforms where instance methods
170     are not descriptors.
171
172     It assumes that the function name and the target attribute name are the
173     same, instead of assuming that the function is a descriptor. This approach
174     is equally fast, but not 100% reliable because functions can be stored on an
175     attribute named differenty than the function's name such as in:
176
177     class A: pass
178     def foo(self): return "foo"
179     A.bar = foo
180
181     But this shouldn't be a common use case. So, on platforms where methods
182     aren't descriptors (such as Jython) this implementation has the advantage
183     of working in the most cases.
184     """
185     def __init__(self, target, onDelete=None):
186         """Return a weak-reference-like instance for a bound method
187
188         target -- the instance-method target for the weak
189             reference, must have im_self and im_func attributes
190             and be reconstructable via:
191                 target.im_func.__get__( target.im_self )
192             which is true of built-in instance methods.
193         onDelete -- optional callback which will be called
194             when this weak reference ceases to be valid
195             (i.e. either the object or the function is garbage
196             collected).  Should take a single argument,
197             which will be passed a pointer to this object.
198         """
199         assert getattr(target.im_self, target.__name__) == target, \
200                ("method %s isn't available as the attribute %s of %s" %
201                 (target, target.__name__, target.im_self))
202         super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete)
203
204     def __call__(self):
205         """Return a strong reference to the bound method
206
207         If the target cannot be retrieved, then will
208         return None, otherwise returns a bound instance
209         method for our object and function.
210
211         Note:
212             You may call this method any number of times,
213             as it does not invalidate the reference.
214         """
215         target = self.weakSelf()
216         if target is not None:
217             function = self.weakFunc()
218             if function is not None:
219                 # Using curry() would be another option, but it erases the
220                 # "signature" of the function. That is, after a function is
221                 # curried, the inspect module can't be used to determine how
222                 # many arguments the function expects, nor what keyword
223                 # arguments it supports, and pydispatcher needs this
224                 # information.
225                 return getattr(target, function.__name__)
226         return None
227
228
229 def get_bound_method_weakref(target, onDelete):
230     """Instantiates the appropiate BoundMethodWeakRef, depending on the details of
231     the underlying class method implementation"""
232     if hasattr(target, '__get__'):
233         # target method is a descriptor, so the default implementation works:
234         return BoundMethodWeakref(target=target, onDelete=onDelete)
235     else:
236         # no luck, use the alternative implementation:
237         return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete)
Note: See TracBrowser for help on using the browser.