My tools of the trade for python programming.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
3.8 KiB

# -*- 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)