Added a weakref proxy implementation that works with both normal functions and instance methods.

This commit is contained in:
Graham Dumpleton
2013-09-13 16:39:11 +10:00
parent d99c296d77
commit 54fc92c9bf
2 changed files with 65 additions and 1 deletions

View File

@@ -1,5 +1,5 @@
__version_info__ = ('1', '1', '0')
__version__ = '.'.join(__version_info__)
from .wrappers import ObjectProxy, FunctionWrapper
from .wrappers import ObjectProxy, FunctionWrapper, WeakFunctionProxy
from .decorators import decorator

View File

@@ -2,6 +2,7 @@ from . import six
import functools
import operator
import weakref
class _ObjectProxyMethods(object):
@@ -511,3 +512,66 @@ try:
from ._wrappers import ObjectProxy, FunctionWrapper
except ImportError:
pass
def _weak_function_proxy_callback(ref, self, callback):
if self._self_expired:
return
self._self_expired = True
# This could raise an exception. We let it propagate back and let
# the weakref.proxy() deal with it, at which point it generally
# prints out a short error message direct to stderr and keeps going.
if callback is not None:
callback(self)
class WeakFunctionProxy(ObjectProxy):
def __init__(self, wrapped, callback=None):
# We need to determine if the wrapped function is actually a
# bound method. In the case of a bound method, we need to keep a
# reference to the original unbound function and the instance.
# This is necessary because if we hold a reference to the bound
# function, it will be the only reference and given it is a
# temporary object, it will almost immediately expire and
# the weakref callback triggered. So what is done is that we
# hold a reference to the instance and unbound function and
# when called bind the function to the instance once again and
# then call it. Note that we avoid using a nested function for
# the callback here so as not to cause any odd reference cycles.
_callback = callback and functools.partial(
_weak_function_proxy_callback, self=self, callback=callback)
self._self_expired = False
try:
self._self_instance = weakref.proxy(wrapped.__self__, _callback)
super(WeakFunctionProxy, self).__init__(
weakref.proxy(wrapped.__func__, _callback))
except AttributeError:
self._self_instance = None
super(WeakFunctionProxy, self).__init__(
weakref.proxy(wrapped, _callback))
def __call__(self, *args, **kwargs):
# We perform a boolean check here on the instance and wrapped
# function as that will trigger the reference error prior to
# calling if the reference had expired.
instance = self._self_instance and self._self_instance
function = self._self_wrapped and self._self_wrapped
# If the wrapped function was originally a bound function, for
# which we retained a reference to the instance and the unbound
# function we need to rebind the function and then call it. If
# not just called the wrapped function.
if instance is None:
return self._self_wrapped(*args, **kwargs)
return function.__get__(instance)(*args, **kwargs)