00001 """Refactored "safe reference" from dispatcher.py"""
00002 import weakref, traceback
00003
00004 def safeRef(target, onDelete = None):
00005 """Return a *safe* weak reference to a callable target
00006
00007 target -- the object to be weakly referenced, if it's a
00008 bound method reference, will create a BoundMethodWeakref,
00009 otherwise creates a simple weakref.
00010 onDelete -- if provided, will have a hard reference stored
00011 to the callable to be called after the safe reference
00012 goes out of scope with the reference object, (either a
00013 weakref or a BoundMethodWeakref) as argument.
00014 """
00015 if hasattr(target, 'im_self'):
00016 if target.im_self is not None:
00017 # Turn a bound method into a BoundMethodWeakref instance.
00018 # Keep track of these instances for lookup by disconnect().
00019 assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
00020 reference = BoundMethodWeakref(
00021 target=target,
00022 onDelete=onDelete
00023 )
00024 return reference
00025 if callable(onDelete):
00026 return weakref.ref(target, onDelete)
00027 else:
00028 return weakref.ref( target )
00029
00030 class BoundMethodWeakref(object):
00031 """'Safe' and reusable weak references to instance methods
00032
00033 BoundMethodWeakref objects provide a mechanism for
00034 referencing a bound method without requiring that the
00035 method object itself (which is normally a transient
00036 object) is kept alive. Instead, the BoundMethodWeakref
00037 object keeps weak references to both the object and the
00038 function which together define the instance method.
00039
00040 Attributes:
00041 key -- the identity key for the reference, calculated
00042 by the class's calculateKey method applied to the
00043 target instance method
00044 deletionMethods -- sequence of callable objects taking
00045 single argument, a reference to this object which
00046 will be called when *either* the target object or
00047 target function is garbage collected (i.e. when
00048 this object becomes invalid). These are specified
00049 as the onDelete parameters of safeRef calls.
00050 weakSelf -- weak reference to the target object
00051 weakFunc -- weak reference to the target function
00052
00053 Class Attributes:
00054 _allInstances -- class attribute pointing to all live
00055 BoundMethodWeakref objects indexed by the class's
00056 calculateKey(target) method applied to the target
00057 objects. This weak value dictionary is used to
00058 short-circuit creation so that multiple references
00059 to the same (object, function) pair produce the
00060 same BoundMethodWeakref instance.
00061
00062 """
00063 _allInstances = weakref.WeakValueDictionary()
00064 def __new__( cls, target, onDelete=None, *arguments,**named ):
00065 """Create new instance or return current instance
00066
00067 Basically this method of construction allows us to
00068 short-circuit creation of references to already-
00069 referenced instance methods. The key corresponding
00070 to the target is calculated, and if there is already
00071 an existing reference, that is returned, with its
00072 deletionMethods attribute updated. Otherwise the
00073 new instance is created and registered in the table
00074 of already-referenced methods.
00075 """
00076 key = cls.calculateKey(target)
00077 current =cls._allInstances.get(key)
00078 if current is not None:
00079 current.deletionMethods.append( onDelete)
00080 return current
00081 else:
00082 base = super( BoundMethodWeakref, cls).__new__( cls )
00083 cls._allInstances[key] = base
00084 base.__init__( target, onDelete, *arguments,**named)
00085 return base
00086 def __init__(self, target, onDelete=None):
00087 """Return a weak-reference-like instance for a bound method
00088
00089 target -- the instance-method target for the weak
00090 reference, must have im_self and im_func attributes
00091 and be reconstructable via:
00092 target.im_func.__get__( target.im_self )
00093 which is true of built-in instance methods.
00094 onDelete -- optional callback which will be called
00095 when this weak reference ceases to be valid
00096 (i.e. either the object or the function is garbage
00097 collected). Should take a single argument,
00098 which will be passed a pointer to this object.
00099 """
00100 def remove(weak, self=self):
00101 """Set self.isDead to true when method or instance is destroyed"""
00102 methods = self.deletionMethods[:]
00103 del self.deletionMethods[:]
00104 try:
00105 del self.__class__._allInstances[ self.key ]
00106 except KeyError:
00107 pass
00108 for function in methods:
00109 try:
00110 if callable( function ):
00111 function( self )
00112 except Exception:
00113 traceback.print_exc()
00114 self.deletionMethods = [onDelete]
00115 self.key = self.calculateKey( target )
00116 self.weakSelf = weakref.ref(target.im_self, remove)
00117 self.weakFunc = weakref.ref(target.im_func, remove)
00118 def calculateKey( cls, target ):
00119 """Calculate the reference key for this reference
00120
00121 Currently this is a two-tuple of the id()'s of the
00122 target object and the target function respectively.
00123 """
00124 return (id(target.im_self),id(target.im_func))
00125 calculateKey = classmethod( calculateKey )
00126 def __str__(self):
00127 """Give a friendly representation of the object"""
00128 return """%s( %s.%s )"""%(
00129 self.__class__.__name__,
00130 self.weakSelf(),
00131 self.weakFunc().__name__,
00132 )
00133 __repr__ = __str__
00134 def __nonzero__( self ):
00135 """Whether we are still a valid reference"""
00136 return self() is not None
00137 def __cmp__( self, other ):
00138 """Compare with another reference"""
00139 if not isinstance (other,self.__class__):
00140 return cmp( self.__class__, type(other) )
00141 return cmp( self.key, other.key)
00142 def __call__(self):
00143 """Return a strong reference to the bound method
00144
00145 If the target cannot be retrieved, then will
00146 return None, otherwise returns a bound instance
00147 method for our object and function.
00148
00149 Note:
00150 You may call this method any number of times,
00151 as it does not invalidate the reference.
00152 """
00153 target = self.weakSelf()
00154 if target is not None:
00155 function = self.weakFunc()
00156 if function is not None:
00157 return function.__get__(target)
00158 return None