Source code for diffpy.srfit.util.weakrefcallable

#!/usr/bin/env python
##############################################################################
#
# diffpy.srfit      Complex Modeling Initiative
#                   (c) 2016 Brookhaven Science Associates,
#                   Brookhaven National Laboratory.
#                   All rights reserved.
#
# File coded by:    Pavol Juhas
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE.txt for license information.
#
##############################################################################
"""Picklable storage of callable objects using weak references."""

import types
import weakref

import six


[docs] class WeakBoundMethod(object): """Callable wrapper to a bound method stored as a weak reference. Support storage of bound methods without keeping the associated objects alive forever. Provide facility for a fallback function to be used when method-related object is deleted. Attributes ---------- function : MethodType the unbound function extracted from the wrapped bound method. fallback : FunctionType, optional plain function to be called when object holding the bound method gets deallocated. The fallback function is called with this object as the first argument followed by any positional and keyword arguments passed for the bound method. The fallback function can be used to deregister this wrapper. _wref : weakref weak reference to the object the wrapped method is bound to. _class : type the type of the object to which the method is bound. This is only used for pickling. """ __slots__ = ("function", "fallback", "_wref", "_class") def __init__(self, f, fallback=None): """Create a weak reference wrapper to bound method. Parameters ---------- f : bound MethodType instance-bound method to be wrapped. fallback : FunctionType, optional plain function to be called instead of the bound method when the method associated object gets deallocated. """ # This does not handle builtin methods, but that can be added # if necessary. self.function = f.__func__ if six.PY2: self.function = f.__func__.__get__(None, f.im_class) self.fallback = fallback self._class = type(f.__self__) self._wref = weakref.ref(f.__self__) return def __call__(self, *args, **kwargs): """Call the wrapped method if the weak-referenced object is alive. If that object does not exist and the fallback function is defined, call the fallback function instead. Parameters ---------- *args, **kwargs same arguments as for the wrapped bound method. Raises ------ ReferenceError when the method-bound object does not exist and the fallback function is not defined. """ mobj = self._wref() if mobj is not None: return self.function(mobj, *args, **kwargs) if self.fallback is not None: return self.fallback(self, *args, **kwargs) emsg = "Object bound to {} does not exist.".format(self.function) raise ReferenceError(emsg) # support use of this class in hashed collections def __hash__(self): return hash((self.function, self._wref)) def __eq__(self, other): rv = self.function == other.function and ( self._wref == other._wref or None is self._wref() is other._wref() ) return rv def __ne__(self, other): return not self.__eq__(other) # support pickling of this type def __getstate__(self): """Return state with a resolved weak reference.""" mobj = self._wref() nm = self.function.__name__ amsg = "Unable to pickle this unbound function by name." if six.PY2: assert self.function == getattr(self._class, nm), amsg if six.PY3: assert self.function is getattr(self._class, nm), amsg state = (self._class, nm, self.fallback, mobj) return state def __setstate__(self, state): """Restore the weak reference in this wrapper upon unpickling.""" self._class, nm, self.fallback, mobj = state self.function = getattr(self._class, nm) if mobj is None: # use a fake weak reference that mimics deallocated object. self._wref = self.__mimic_empty_ref return # Here the referred object exists. self._wref = weakref.ref(mobj) return @staticmethod def __mimic_empty_ref(): return None
# end of class WeakBoundMethod
[docs] def weak_ref(f, fallback=None): """Create weak-reference wrapper to a bound method. Parameters ---------- f : callable object-bound method or a plain function. fallback : FunctionType, optional plain function to be called when object holding ``f`` gets deallocated. The fallback function is called with the wrapper object as the first argument followed by positional and keyword arguments passed for the bound method. The fallback function can be used to deregister the wrapper. Returns ------- WeakBoundMethod when `f` is a bound method. If `f` is a plain function or already of WeakBoundMethod type, return `f` and ignore the `fallback` argument. """ # NOTE Weak referencing plain functions is probably not needed, # because they are already bound to the defining modules. rv = f if isinstance(f, (types.MethodType, types.BuiltinMethodType)): rv = WeakBoundMethod(f, fallback=fallback) return rv