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