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]