saferef.py

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

Generated on Thu Apr 27 20:52:43 2006 for LICOS L02-01-00 by doxygen 1.4.6-NO