import types
# Fill in a flattened method resolution dictionary for a class (attributes are
# filtered out). Flattening honours the MI method resolution rules
# (depth-first search of bases in order). The dictionary has method names
# for keys and functions for values.
def __methodDict(cls, dict):
# the strategy is to traverse the class in the _reverse_ of the normal
# order, and overwrite any duplicates.
baseList = list(cls.__bases__)
baseList.reverse()
# do bases in reverse order, so first base overrides last base
for super in baseList:
__methodDict(super, dict)
# do my methods last to override base classes
for key, value in cls.__dict__.items():
# ignore class attributes
if type(value) == types.FunctionType:
dict[key] = value
def __methods(cls):
# Return all method names for a class.
# Return all method names for a class (attributes are filtered
# out). Base classes are searched recursively.
dict = {}
__methodDict(cls, dict)
return dict.keys()
# Function body to resolve a forwarding given the target method name and the
# attribute name. The resulting lambda requires only self, but will forward
# any other parameters.
__stringBody = (
'def %(method)s(this, *args, **kw): return ' +
'apply(this.%(attribute)s.%(method)s, args, kw)')
# Get a unique id
__counter = 0
def __unique():
global __counter
__counter = __counter + 1
return str(__counter)
# Function body to resolve a forwarding given the target method name and the
# index of the resolution function. The resulting lambda requires only self,
# but will forward any other parameters. The target instance is identified
# by invoking the resolution function.
__funcBody = (
'def %(method)s(this, *args, **kw): return ' +
'apply(this.%(forwardFunc)s().%(method)s, args, kw)')
def forwardmethods(fromClass, toClass, toPart, exclude = []):
# Forward all methods from one class to another.
# Forwarders will be created in fromClass to forward method
# invocations to toClass. The methods to be forwarded are
# identified by flattening the interface of toClass, and excluding
# methods identified in the exclude list. Methods already defined
# in fromClass, or special methods with one or more leading or
# trailing underscores will not be forwarded.
# For a given object of class fromClass, the corresponding toClass
# object is identified using toPart. This can either be a String
# denoting an attribute of fromClass objects, or a function taking
# a fromClass object and returning a toClass object.
# Example:
# class MyClass:
# ...
# def __init__(self):
# ...
# self.__target = TargetClass()
# ...
# def findtarget(self):
# return self.__target
# forwardmethods(MyClass, TargetClass, '__target', ['dangerous1', 'dangerous2'])
# # ...or...
# forwardmethods(MyClass, TargetClass, MyClass.findtarget,
# ['dangerous1', 'dangerous2'])
# In both cases, all TargetClass methods will be forwarded from
# MyClass except for dangerous1, dangerous2, special methods like
# __str__, and pre-existing methods like findtarget.
# Allow an attribute name (String) or a function to determine the instance
if type(toPart) != types.StringType:
# check that it is something like a function
if callable(toPart):
# If a method is passed, use the function within it
if hasattr(toPart, 'im_func'):
toPart = toPart.im_func
# After this is set up, forwarders in this class will use
# the forwarding function. The forwarding function name is
# guaranteed to be unique, so that it can't be hidden by subclasses
forwardName = '__fwdfunc__' + __unique()
fromClass.__dict__[forwardName] = toPart
# It's not a valid type
else:
raise TypeError, 'toPart must be attribute name, function or method'
# get the full set of candidate methods
dict = {}
__methodDict(toClass, dict)
# discard special methods
for ex in dict.keys():
if ex[:1] == '_' or ex[-1:] == '_':
del dict[ex]
# discard dangerous methods supplied by the caller
for ex in exclude:
if dict.has_key(ex):
del dict[ex]
# discard methods already defined in fromClass
for ex in __methods(fromClass):
if dict.has_key(ex):
del dict[ex]
for method, func in dict.items():
d = {'method': method, 'func': func}
if type(toPart) == types.StringType:
execString = \
__stringBody % {'method' : method, 'attribute' : toPart}
else:
execString = \
__funcBody % {'forwardFunc' : forwardName, 'method' : method}
exec execString in d
# this creates a method
fromClass.__dict__[method] = d[method]