You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
10 KiB
250 lines
10 KiB
""" |
|
"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)
|
|
|