PageRenderTime 377ms CodeModel.GetById 231ms app.highlight 12ms RepoModel.GetById 130ms app.codeStats 0ms

/django/dispatch/saferef.py

https://code.google.com/p/mango-py/
Python | 250 lines | 241 code | 1 blank | 8 comment | 1 complexity | 8d89fdb16d979ba7d43cb98e02e9c734 MD5 | raw file
  1"""
  2"Safe weakrefs", originally from pyDispatcher.
  3
  4Provides a way to safely weakref any function, including bound methods (which
  5aren't handled by the core weakref module).
  6"""
  7
  8import weakref, traceback
  9
 10def 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
 36class 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
182class 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
242def 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)