app/django/dispatch/saferef.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     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)
       
   238