From 7568f5874ee125a5d18294503334e8e92a7eccbf Mon Sep 17 00:00:00 2001 From: Wirawan Purwanto Date: Mon, 16 Mar 2015 18:07:18 -0400 Subject: [PATCH] * Added a tool to create bound instance method with weak ref to the instance. --- py/__init__.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++- py/im_weakref.py | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/py/__init__.py b/py/__init__.py index c3e2ca0..8c91006 100644 --- a/py/__init__.py +++ b/py/__init__.py @@ -10,10 +10,30 @@ import sys import new +""" +wpylib.py module +Collection of low-level pythonic hacks + +Instance method hacks +--------------------- + +make_unbound_instance_method:: +make_unbound_method:: + +One possible reason of using these routines is to provide an +alterable calling point with different implementations--somewhat like +virtual methods, but something that can be changed dynamically +on-the-fly. +NOTE: The trick provided by wpylib.py.im_ref.im_ref class is +a better way to accomplish the same thing. + + +""" def make_unbound_instance_method(method): """Generates an unbound instance method from a possibly bound - instance method.""" + instance method. + """ return new.instancemethod(method.im_func, None, method.im_class) @@ -32,6 +52,41 @@ def make_unbound_method(method): return method +def make_weakly_bound_method(method, instance): + """Creates a bound instance method, where the instance is weakly + referred. + + This trick is necessary if the bound method is to be attached + to that instance's attribute list (e.g. via instance.__dict__), + because otherwise a circular reference occurs: + + bound_method -> instance -> bound_method (via __dict__) + + CAVEAT: Know what you are doing! In general, this is a haram trick. + We circumvent the standard class type-safety mechanism in python here: + if the `method` actually belongs to a completely unrelated class, + this routine still accepts it and allow the function to be called. + """ + # NOTE: This is an identical trick to wpylib.py.im_ref.xbound_im_ref; + # the latter (the OO interface) is better because + # it does not need function closure, and that the object data is + # easier to diagnose. + from weakref import ref + instance_ref = ref(instance) + instance_cls = instance.__class__ + try: + im_func, im_class = method.im_func, method.im_class + #im_method = new.instancemethod(im_func, None, instance_cls) + except AttributeError: + im_func = method + # Assume this is a function defined outside a class, which is then + # injected into this instance. + # The first argument must be the usual `self` argument. + return lambda *args, **kwargs: method(instance_ref(), *args, **kwargs) + + return lambda *args, **kwargs: im_func(instance_ref(), *args, **kwargs) + + def function_name(f): """Returns the given name of a function (or callable object).""" try: diff --git a/py/im_weakref.py b/py/im_weakref.py index d0bae80..c5e563c 100644 --- a/py/im_weakref.py +++ b/py/im_weakref.py @@ -15,6 +15,13 @@ wpylib.py.im_weakref Complement of weakref's weak reference for an instance method (whether bound or unbound). +This trick is necessary if the bound instance method is to be attached +to that instance's attribute list (e.g. on `instance.__dict__`), +because otherwise a circular reference occurs: + + bound_method -> instance -> bound_method (via __dict__) + + Original-source: Linux Screen Reader project Copied-from: http://mindtrove.info/python-weak-references/ Date: 20110607 @@ -84,3 +91,34 @@ class im_ref(object): Inverse of __eq__. ''' return not self.__eq__(other) + + +class xbound_im_ref(im_ref): + '''A dirty hack to make an im_ref object where the callable can be + an instance method belonging to a completely different class, or + to an ordinary function. + + CAUTION: Know what you are doing! In general, this is a haram trick. + This object is used for forced injection of an external method as + a class method. + We circumvent the standard class type-safety mechanism in python here: + if the `method` actually belongs to a completely unrelated class, + this routine still accepts it and allow the function to be called. + ''' + def __init__(self, method, instance): + self.inst = weakref.ref(instance) + self.klass = instance.__class__ + + try: + self.func, im_class = method.im_func, method.im_class + except AttributeError: + # Assume this is a function defined outside a class, which is then + # injected into this instance. + # The first argument must be the usual `self` argument. + self.func = method + + def __call__(self, *args, **kwargs): + if self.inst is not None and self.inst() is None: + raise ReferenceError, "Original object (of type %s) is already dead." % (self.klass) + return self.func(self.inst(), *args, **kwargs) +