/django/dispatch/saferef.py
Python | 250 lines | 241 code | 1 blank | 8 comment | 2 complexity | 8d89fdb16d979ba7d43cb98e02e9c734 MD5 | raw file
Possible License(s): BSD-3-Clause
- """
- "Safe weakrefs", originally from pyDispatcher.
- Provides a way to safely weakref any function, including bound methods (which
- aren't handled by the core weakref module).
- """
- import weakref, traceback
- def safeRef(target, onDelete = None):
- """Return a *safe* weak reference to a callable target
- target -- the object to be weakly referenced, if it's a
- bound method reference, will create a BoundMethodWeakref,
- otherwise creates a simple weakref.
- onDelete -- if provided, will have a hard reference stored
- to the callable to be called after the safe reference
- goes out of scope with the reference object, (either a
- weakref or a BoundMethodWeakref) as argument.
- """
- if hasattr(target, 'im_self'):
- if target.im_self is not None:
- # Turn a bound method into a BoundMethodWeakref instance.
- # Keep track of these instances for lookup by disconnect().
- assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
- reference = get_bound_method_weakref(
- target=target,
- onDelete=onDelete
- )
- return reference
- if callable(onDelete):
- return weakref.ref(target, onDelete)
- else:
- return weakref.ref( target )
- class BoundMethodWeakref(object):
- """'Safe' and reusable weak references to instance methods
- BoundMethodWeakref objects provide a mechanism for
- referencing a bound method without requiring that the
- method object itself (which is normally a transient
- object) is kept alive. Instead, the BoundMethodWeakref
- object keeps weak references to both the object and the
- function which together define the instance method.
- Attributes:
- key -- the identity key for the reference, calculated
- by the class's calculateKey method applied to the
- target instance method
- deletionMethods -- sequence of callable objects taking
- single argument, a reference to this object which
- will be called when *either* the target object or
- target function is garbage collected (i.e. when
- this object becomes invalid). These are specified
- as the onDelete parameters of safeRef calls.
- weakSelf -- weak reference to the target object
- weakFunc -- weak reference to the target function
- Class Attributes:
- _allInstances -- class attribute pointing to all live
- BoundMethodWeakref objects indexed by the class's
- calculateKey(target) method applied to the target
- objects. This weak value dictionary is used to
- short-circuit creation so that multiple references
- to the same (object, function) pair produce the
- same BoundMethodWeakref instance.
- """
-
- _allInstances = weakref.WeakValueDictionary()
-
- def __new__( cls, target, onDelete=None, *arguments,**named ):
- """Create new instance or return current instance
- Basically this method of construction allows us to
- short-circuit creation of references to already-
- referenced instance methods. The key corresponding
- to the target is calculated, and if there is already
- an existing reference, that is returned, with its
- deletionMethods attribute updated. Otherwise the
- new instance is created and registered in the table
- of already-referenced methods.
- """
- key = cls.calculateKey(target)
- current =cls._allInstances.get(key)
- if current is not None:
- current.deletionMethods.append( onDelete)
- return current
- else:
- base = super( BoundMethodWeakref, cls).__new__( cls )
- cls._allInstances[key] = base
- base.__init__( target, onDelete, *arguments,**named)
- return base
-
- def __init__(self, target, onDelete=None):
- """Return a weak-reference-like instance for a bound method
- target -- the instance-method target for the weak
- reference, must have im_self and im_func attributes
- and be reconstructable via:
- target.im_func.__get__( target.im_self )
- which is true of built-in instance methods.
- onDelete -- optional callback which will be called
- when this weak reference ceases to be valid
- (i.e. either the object or the function is garbage
- collected). Should take a single argument,
- which will be passed a pointer to this object.
- """
- def remove(weak, self=self):
- """Set self.isDead to true when method or instance is destroyed"""
- methods = self.deletionMethods[:]
- del self.deletionMethods[:]
- try:
- del self.__class__._allInstances[ self.key ]
- except KeyError:
- pass
- for function in methods:
- try:
- if callable( function ):
- function( self )
- except Exception, e:
- try:
- traceback.print_exc()
- except AttributeError, err:
- print '''Exception during saferef %s cleanup function %s: %s'''%(
- self, function, e
- )
- self.deletionMethods = [onDelete]
- self.key = self.calculateKey( target )
- self.weakSelf = weakref.ref(target.im_self, remove)
- self.weakFunc = weakref.ref(target.im_func, remove)
- self.selfName = str(target.im_self)
- self.funcName = str(target.im_func.__name__)
-
- def calculateKey( cls, target ):
- """Calculate the reference key for this reference
- Currently this is a two-tuple of the id()'s of the
- target object and the target function respectively.
- """
- return (id(target.im_self),id(target.im_func))
- calculateKey = classmethod( calculateKey )
-
- def __str__(self):
- """Give a friendly representation of the object"""
- return """%s( %s.%s )"""%(
- self.__class__.__name__,
- self.selfName,
- self.funcName,
- )
-
- __repr__ = __str__
-
- def __nonzero__( self ):
- """Whether we are still a valid reference"""
- return self() is not None
-
- def __cmp__( self, other ):
- """Compare with another reference"""
- if not isinstance (other,self.__class__):
- return cmp( self.__class__, type(other) )
- return cmp( self.key, other.key)
-
- def __call__(self):
- """Return a strong reference to the bound method
- If the target cannot be retrieved, then will
- return None, otherwise returns a bound instance
- method for our object and function.
- Note:
- You may call this method any number of times,
- as it does not invalidate the reference.
- """
- target = self.weakSelf()
- if target is not None:
- function = self.weakFunc()
- if function is not None:
- return function.__get__(target)
- return None
- class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
- """A specialized BoundMethodWeakref, for platforms where instance methods
- are not descriptors.
- It assumes that the function name and the target attribute name are the
- same, instead of assuming that the function is a descriptor. This approach
- is equally fast, but not 100% reliable because functions can be stored on an
- attribute named differenty than the function's name such as in:
- class A: pass
- def foo(self): return "foo"
- A.bar = foo
- But this shouldn't be a common use case. So, on platforms where methods
- aren't descriptors (such as Jython) this implementation has the advantage
- of working in the most cases.
- """
- def __init__(self, target, onDelete=None):
- """Return a weak-reference-like instance for a bound method
- target -- the instance-method target for the weak
- reference, must have im_self and im_func attributes
- and be reconstructable via:
- target.im_func.__get__( target.im_self )
- which is true of built-in instance methods.
- onDelete -- optional callback which will be called
- when this weak reference ceases to be valid
- (i.e. either the object or the function is garbage
- collected). Should take a single argument,
- which will be passed a pointer to this object.
- """
- assert getattr(target.im_self, target.__name__) == target, \
- ("method %s isn't available as the attribute %s of %s" %
- (target, target.__name__, target.im_self))
- super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete)
- def __call__(self):
- """Return a strong reference to the bound method
- If the target cannot be retrieved, then will
- return None, otherwise returns a bound instance
- method for our object and function.
- Note:
- You may call this method any number of times,
- as it does not invalidate the reference.
- """
- target = self.weakSelf()
- if target is not None:
- function = self.weakFunc()
- if function is not None:
- # Using curry() would be another option, but it erases the
- # "signature" of the function. That is, after a function is
- # curried, the inspect module can't be used to determine how
- # many arguments the function expects, nor what keyword
- # arguments it supports, and pydispatcher needs this
- # information.
- return getattr(target, function.__name__)
- return None
- def get_bound_method_weakref(target, onDelete):
- """Instantiates the appropiate BoundMethodWeakRef, depending on the details of
- the underlying class method implementation"""
- if hasattr(target, '__get__'):
- # target method is a descriptor, so the default implementation works:
- return BoundMethodWeakref(target=target, onDelete=onDelete)
- else:
- # no luck, use the alternative implementation:
- return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete)