web/lib/django/dispatch/saferef.py
changeset 0 0d40e90630ef
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     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)