Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

saferef.py

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

Generated on Fri Jan 21 18:00:52 2005 for LATTE P04-06-05 by doxygen 1.4.1