0001"""Refactored 'safe reference from dispatcher.py"""
0002
0003import weakref
0004import traceback
0005
0006
0007def safe_ref(target, on_delete=None):
0008 """Return a *safe* weak reference to a callable target.
0009
0010 - ``target``: The object to be weakly referenced, if it's a bound
0011 method reference, will create a BoundMethodWeakref, otherwise
0012 creates a simple weakref.
0013
0014 - ``on_delete``: If provided, will have a hard reference stored to
0015 the callable to be called after the safe reference goes out of
0016 scope with the reference object, (either a weakref or a
0017 BoundMethodWeakref) as argument.
0018 """
0019 if hasattr(target, 'im_self'):
0020 if target.im_self is not None:
0021
0022
0023 assert hasattr(target, 'im_func'), (
0024 "safe_ref target %r has im_self, but no im_func, "
0025 "don't know how to create reference"
0026 % target
0027 )
0028 reference = BoundMethodWeakref(target=target, on_delete=on_delete)
0029 return reference
0030 if callable(on_delete):
0031 return weakref.ref(target, on_delete)
0032 else:
0033 return weakref.ref(target)
0034
0035
0036class BoundMethodWeakref(object):
0037 """'Safe' and reusable weak references to instance methods.
0038
0039 BoundMethodWeakref objects provide a mechanism for referencing a
0040 bound method without requiring that the method object itself
0041 (which is normally a transient object) is kept alive. Instead,
0042 the BoundMethodWeakref object keeps weak references to both the
0043 object and the function which together define the instance method.
0044
0045 Attributes:
0046
0047 - ``key``: The identity key for the reference, calculated by the
0048 class's calculate_key method applied to the target instance method.
0049
0050 - ``deletion_methods``: Sequence of callable objects taking single
0051 argument, a reference to this object which will be called when
0052 *either* the target object or target function is garbage
0053 collected (i.e. when this object becomes invalid). These are
0054 specified as the on_delete parameters of safe_ref calls.
0055
0056 - ``weak_self``: Weak reference to the target object.
0057
0058 - ``weak_func``: Weak reference to the target function.
0059
0060 Class Attributes:
0061
0062 - ``_all_instances``: Class attribute pointing to all live
0063 BoundMethodWeakref objects indexed by the class's
0064 calculate_key(target) method applied to the target objects.
0065 This weak value dictionary is used to short-circuit creation so
0066 that multiple references to the same (object, function) pair
0067 produce the same BoundMethodWeakref instance.
0068 """
0069
0070 _all_instances = weakref.WeakValueDictionary()
0071
0072 def __new__(cls, target, on_delete=None, *arguments, **named):
0073 """Create new instance or return current instance.
0074
0075 Basically this method of construction allows us to
0076 short-circuit creation of references to already- referenced
0077 instance methods. The key corresponding to the target is
0078 calculated, and if there is already an existing reference,
0079 that is returned, with its deletion_methods attribute updated.
0080 Otherwise the new instance is created and registered in the
0081 table of already-referenced methods.
0082 """
0083 key = cls.calculate_key(target)
0084 current = cls._all_instances.get(key)
0085 if current is not None:
0086 current.deletion_methods.append(on_delete)
0087 return current
0088 else:
0089 base = super(BoundMethodWeakref, cls).__new__(cls)
0090 cls._all_instances[key] = base
0091 base.__init__(target, on_delete, *arguments, **named)
0092 return base
0093
0094 def __init__(self, target, on_delete=None):
0095 """Return a weak-reference-like instance for a bound method.
0096
0097 - ``target``: The instance-method target for the weak reference,
0098 must have im_self and im_func attributes and be
0099 reconstructable via the following, which is true of built-in
0100 instance methods::
0101
0102 target.im_func.__get__( target.im_self )
0103
0104 - ``on_delete``: Optional callback which will be called when
0105 this weak reference ceases to be valid (i.e. either the
0106 object or the function is garbage collected). Should take a
0107 single argument, which will be passed a pointer to this
0108 object.
0109 """
0110 def remove(weak, self=self):
0111 """Set self.isDead to True when method or instance is destroyed."""
0112 methods = self.deletion_methods[:]
0113 del self.deletion_methods[:]
0114 try:
0115 del self.__class__._all_instances[self.key]
0116 except KeyError:
0117 pass
0118 for function in methods:
0119 try:
0120 if callable(function):
0121 function(self)
0122 except Exception:
0123 try:
0124 traceback.print_exc()
0125 except AttributeError, e:
0126 print ('Exception during saferef %s '
0127 'cleanup function %s: %s' % (self, function, e))
0128 self.deletion_methods = [on_delete]
0129 self.key = self.calculate_key(target)
0130 self.weak_self = weakref.ref(target.im_self, remove)
0131 self.weak_func = weakref.ref(target.im_func, remove)
0132 self.self_name = str(target.im_self)
0133 self.func_name = str(target.im_func.__name__)
0134
0135 def calculate_key(cls, target):
0136 """Calculate the reference key for this reference.
0137
0138 Currently this is a two-tuple of the id()'s of the target
0139 object and the target function respectively.
0140 """
0141 return (id(target.im_self), id(target.im_func))
0142 calculate_key = classmethod(calculate_key)
0143
0144 def __str__(self):
0145 """Give a friendly representation of the object."""
0146 return "%s(%s.%s)" % (
0147 self.__class__.__name__,
0148 self.self_name,
0149 self.func_name,
0150 )
0151
0152 __repr__ = __str__
0153
0154 def __nonzero__(self):
0155 """Whether we are still a valid reference."""
0156 return self() is not None
0157
0158 def __cmp__(self, other):
0159 """Compare with another reference."""
0160 if not isinstance(other, self.__class__):
0161 return cmp(self.__class__, type(other))
0162 return cmp(self.key, other.key)
0163
0164 def __call__(self):
0165 """Return a strong reference to the bound method.
0166
0167 If the target cannot be retrieved, then will return None,
0168 otherwise returns a bound instance method for our object and
0169 function.
0170
0171 Note: You may call this method any number of times, as it does
0172 not invalidate the reference.
0173 """
0174 target = self.weak_self()
0175 if target is not None:
0176 function = self.weak_func()
0177 if function is not None:
0178 return function.__get__(target)
0179 return None