Ticket #5664: saferef_for_non_descriptor_methods.patch

File saferef_for_non_descriptor_methods.patch, 4.3 KB (added by leosoto <leo.soto@…>, 8 years ago)

Patch that offer a different implementation for platforms where methods are not descriptors. It has no impact when running under CPython.

  • django/dispatch/saferef.py

    diff -r e01b520b591b django/dispatch/saferef.py
    a b  
    11"""Refactored "safe reference" from dispatcher.py"""
    22import weakref, traceback
     3from django.utils.functional import curry
    34
    45def safeRef(target, onDelete = None):
    56    """Return a *safe* weak reference to a callable target
    def safeRef(target, onDelete = None): 
    1718            # Turn a bound method into a BoundMethodWeakref instance.
    1819            # Keep track of these instances for lookup by disconnect().
    1920            assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
    20             reference = BoundMethodWeakref(
     21            reference = get_bound_method_weakref(
    2122                target=target,
    2223                onDelete=onDelete
    2324            )
    class BoundMethodWeakref(object): 
    163164            if function is not None:
    164165                return function.__get__(target)
    165166        return None
     167
     168class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
     169    """A specialized BoundMethodWeakref, for platforms where instance methods
     170    are not descriptors.
     171
     172    It assumes that the function name and the target attribute name are the
     173    same, instead of assuming that the function is a descriptor. This approach
     174    is equally fast, but not 100% reliable because functions can be stored on an
     175    attribute named differenty than the function's name such as in:
     176
     177    class A: pass
     178    def foo(self): return "foo"
     179    A.bar = foo
     180
     181    But this shouldn't be a common use case. So, on platforms where methods
     182    aren't descriptors (such as Jython) this implementation has the advantage
     183    of working in the most cases.
     184    """
     185    def __init__(self, target, onDelete=None):
     186        """Return a weak-reference-like instance for a bound method
     187
     188        target -- the instance-method target for the weak
     189            reference, must have im_self and im_func attributes
     190            and be reconstructable via:
     191                target.im_func.__get__( target.im_self )
     192            which is true of built-in instance methods.
     193        onDelete -- optional callback which will be called
     194            when this weak reference ceases to be valid
     195            (i.e. either the object or the function is garbage
     196            collected).  Should take a single argument,
     197            which will be passed a pointer to this object.
     198        """
     199        assert getattr(target.im_self, target.__name__) == target, \
     200               ("method %s isn't available as the attribute %s of %s" %
     201                (target, target.__name__, target.im_self))
     202        super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete)
     203
     204    def __call__(self):
     205        """Return a strong reference to the bound method
     206
     207        If the target cannot be retrieved, then will
     208        return None, otherwise returns a bound instance
     209        method for our object and function.
     210
     211        Note:
     212            You may call this method any number of times,
     213            as it does not invalidate the reference.
     214        """
     215        target = self.weakSelf()
     216        if target is not None:
     217            function = self.weakFunc()
     218            if function is not None:
     219                # Using curry() would be another option, but it erases the
     220                # "signature" of the function. That is, after a function is
     221                # curried, the inspect module can't be used to determine how
     222                # many arguments the function expects, nor what keyword
     223                # arguments it supports, and pydispatcher needs this
     224                # information.
     225                return getattr(target, function.__name__)
     226        return None
     227
     228
     229def get_bound_method_weakref(target, onDelete):
     230    """Instantiates the appropiate BoundMethodWeakRef, depending on the details of
     231    the underlying class method implementation"""
     232    if hasattr(target, '__get__'):
     233        # target method is a descriptor, so the default implementation works:
     234        return BoundMethodWeakref(target=target, onDelete=onDelete)
     235    else:
     236        # no luck, use the alternative implementation:
     237        return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete)
     238
Back to Top