|
|
|
# -*- python -*-
|
|
|
|
#
|
|
|
|
# $Id: im_weakref.py,v 1.2 2011-09-07 15:04:15 wirawan Exp $
|
|
|
|
#
|
|
|
|
# wpylib.py.im_weakref
|
|
|
|
# Created: 20110607
|
|
|
|
# Wirawan Purwanto
|
|
|
|
#
|
|
|
|
# Advanced hack tools for python: weakref proxy class for instance-method.
|
|
|
|
#
|
|
|
|
|
|
|
|
"""
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
|
|
|
|
import weakref, new
|
|
|
|
|
|
|
|
class im_ref(object):
|
|
|
|
'''
|
|
|
|
Our own proxy object which enables weak references to bound and unbound
|
|
|
|
methods and arbitrary callables. Pulls information about the function,
|
|
|
|
class, and instance out of a bound method. Stores a weak reference to the
|
|
|
|
instance to support garbage collection.
|
|
|
|
|
|
|
|
@organization: IBM Corporation
|
|
|
|
@copyright: Copyright (c) 2005, 2006 IBM Corporation
|
|
|
|
@license: The BSD License
|
|
|
|
'''
|
|
|
|
def __init__(self, cb):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
self.inst = weakref.ref(cb.im_self)
|
|
|
|
except TypeError:
|
|
|
|
self.inst = None
|
|
|
|
self.func = cb.im_func
|
|
|
|
self.klass = cb.im_class
|
|
|
|
except AttributeError:
|
|
|
|
self.inst = None
|
|
|
|
self.func = cb.im_func
|
|
|
|
self.klass = None
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
|
|
'''
|
|
|
|
Proxy for a call to the weak referenced object. Take arbitrary params to
|
|
|
|
pass to the callable.
|
|
|
|
|
|
|
|
@raise ReferenceError: When the weak reference refers to a dead object
|
|
|
|
'''
|
|
|
|
if self.inst is not None and self.inst() is None:
|
|
|
|
raise ReferenceError, "Original object (of type %s) is already dead." % (self.klass)
|
|
|
|
elif self.inst is not None:
|
|
|
|
# build a new instance method with a strong reference to the instance
|
|
|
|
mtd = new.instancemethod(self.func, self.inst(), self.klass)
|
|
|
|
else:
|
|
|
|
# not a bound method, just return the func
|
|
|
|
mtd = self.func
|
|
|
|
# invoke the callable and return the result
|
|
|
|
return mtd(*args, **kwargs)
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
'''
|
|
|
|
Compare the held function and instance with that held by another proxy.
|
|
|
|
|
|
|
|
@param other: Another proxy object
|
|
|
|
@type other: L{Proxy}
|
|
|
|
@return: Whether this func/inst pair is equal to the one in the other
|
|
|
|
proxy object or not
|
|
|
|
@rtype: boolean
|
|
|
|
'''
|
|
|
|
try:
|
|
|
|
return self.func == other.func and self.inst() == other.inst()
|
|
|
|
except Exception:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def __ne__(self, other):
|
|
|
|
'''
|
|
|
|
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)
|
|
|
|
|