<reponame>tdiprima/code<filename>recipes/Python/363782_InverseExtendGenerators/recipe-363782.py
"""Iterate downward through a hierarchy calling a method at each step."""

__docformat__ = "restructuredtext"

import types


def inverseExtend(boundMethod, *args, **kargs):

    """Iterate downward through a hierarchy calling a method at each step.

    boundMethod -- This is the bound method of the object you're interested in.
    args, kargs -- The arguments and keyword arguments to pass to the
        top-level method.

    You can call this method via something like this:

        inverseExtend(object.method, myArg, myOtherArg)

    When calling the method at each step, I'll call it like this: 

        Class.method(object, *args, **kargs)

    Each parent class method *must* be a generator with exactly one yield
    statement (even if the yield statement never actually gets called), but the
    lowest level class method must *not* be a generator.  In the parent class:
    
        yield args, kargs

    should be called when it is time to transfer control to the subclass.  This
    may be in the middle of the method or not at all if the parent class does
    not wish for the child class's method to get a chance to run.  

    """

    # Build all the necessary data structures.

    obj = boundMethod.im_self
    methodName = boundMethod.im_func.__name__

    # Figure out the classes in the class hierarchy.  "classes" will
    # contain the most senior classes first.

    Class = obj.__class__
    classes = [Class]
    while Class.__bases__:
        Class = Class.__bases__[0]
        classes.insert(0, Class)

    # Skip classes that don't define the method.  Be careful with getattr
    # since it automatically looks in parent classes.  

    last = None
    methods = []
    for Class in classes:
        if (hasattr(Class, methodName) and 
            getattr(Class, methodName) != last):
            last = getattr(Class, methodName)
            methods.append(last)

    # Traverse down the class hierarchy.  Watch out for StopIteration's which
    # signify that the parent does not wish to call the child class's method.
    # generatorMethods maps generators to methods which we'll need for nice
    # error messages.

    generators = []
    generatorMethods = {}
    for method in methods[:-1]:
        generator = method(obj, *args, **kargs)
        assert isinstance(generator, types.GeneratorType), \
            "%s must be a generator" % `method`
        try:
            (args, kargs) = generator.next()
        except StopIteration:
            break
        generators.insert(0, generator)
        generatorMethods[generator] = method

    # If we didn't have to break, then the lowest level class's method gets to
    # run.

    else:
        method = methods[-1]
        ret = method(obj, *args, **kargs)
        assert not isinstance(ret, types.GeneratorType), \
            "%s must not be a generator" % method

    # Traverse back up the class hierarchy.  We should get StopIteration's at
    # every step.

    for generator in generators:
        try:
            generator.next()
            raise AssertionError("%s has more than one yield statement" %
                                 `generatorMethods[generator]`)
        except StopIteration:
            pass


# Test out the code.
if __name__ == "__main__":

    from cStringIO import StringIO

    class A:
        def f(self, count):
            buf.write('<A count="%s">\n' % count)
            yield (count + 1,), {}
            buf.write('</A>')

    class B(A):
        # I don't have an f method, so you can skip me.
        pass

    class C(B):
        def f(self, count):
            buf.write('  <C count="%s">\n' % count)
            yield (count + 1,), {}
            buf.write('  </C>\n')

    class D(C):
        def f(self, count):
            buf.write('    <D count="%s" />\n' % count)

    expected = """\
<A count="0">
  <C count="1">
    <D count="2" />
  </C>
</A>"""

    buf = StringIO()
    d = D()
    inverseExtend(d.f, 0)
    assert buf.getvalue() == expected
    buf.close()

"""Iterate downward through a hierarchy calling a method at each step."""

__docformat__ = "restructuredtext"

import types


def inverseExtend(boundMethod, *args, **kargs):

    """Iterate downward through a hierarchy calling a method at each step.

    boundMethod -- This is the bound method of the object you're interested in.
    args, kargs -- The arguments and keyword arguments to pass to the
        top-level method.

    You can call this method via something like this:

        inverseExtend(object.method, myArg, myOtherArg)

    When calling the method at each step, I'll call it like this: 

        Class.method(object, *args, **kargs)

    Each parent class method *must* be a generator with exactly one yield
    statement (even if the yield statement never actually gets called), but the
    lowest level class method must *not* be a generator.  In the parent class:
    
        yield args, kargs

    should be called when it is time to transfer control to the subclass.  This
    may be in the middle of the method or not at all if the parent class does
    not wish for the child class's method to get a chance to run.  

    """

    # Build all the necessary data structures.

    obj = boundMethod.im_self
    methodName = boundMethod.im_func.__name__

    # Figure out the classes in the class hierarchy.  "classes" will
    # contain the most senior classes first.

    Class = obj.__class__
    classes = [Class]
    while Class.__bases__:
        Class = Class.__bases__[0]
        classes.insert(0, Class)

    # Skip classes that don't define the method.  Be careful with getattr
    # since it automatically looks in parent classes.  

    last = None
    methods = []
    for Class in classes:
        if (hasattr(Class, methodName) and 
            getattr(Class, methodName) != last):
            last = getattr(Class, methodName)
            methods.append(last)

    # Traverse down the class hierarchy.  Watch out for StopIteration's which
    # signify that the parent does not wish to call the child class's method.
    # generatorMethods maps generators to methods which we'll need for nice
    # error messages.

    generators = []
    generatorMethods = {}
    for method in methods[:-1]:
        generator = method(obj, *args, **kargs)
        assert isinstance(generator, types.GeneratorType), \
            "%s must be a generator" % `method`
        try:
            (args, kargs) = generator.next()
        except StopIteration:
            break
        generators.insert(0, generator)
        generatorMethods[generator] = method

    # If we didn't have to break, then the lowest level class's method gets to
    # run.

    else:
        method = methods[-1]
        ret = method(obj, *args, **kargs)
        assert not isinstance(ret, types.GeneratorType), \
            "%s must not be a generator" % method

    # Traverse back up the class hierarchy.  We should get StopIteration's at
    # every step.

    for generator in generators:
        try:
            generator.next()
            raise AssertionError("%s has more than one yield statement" %
                                 `generatorMethods[generator]`)
        except StopIteration:
            pass


# Test out the code.
if __name__ == "__main__":

    from cStringIO import StringIO

    class A:
        def f(self, count):
            buf.write('<A count="%s">\n' % count)
            yield (count + 1,), {}
            buf.write('</A>')

    class B(A):
        # I don't have an f method, so you can skip me.
        pass

    class C(B):
        def f(self, count):
            buf.write('  <C count="%s">\n' % count)
            yield (count + 1,), {}
            buf.write('  </C>\n')

    class D(C):
        def f(self, count):
            buf.write('    <D count="%s" />\n' % count)

    expected = """\
<A count="0">
  <C count="1">
    <D count="2" />
  </C>
</A>"""

    buf = StringIO()
    d = D()
    inverseExtend(d.f, 0)
    assert buf.getvalue() == expected
    buf.close()
Iterate downward through a hierarchy calling a method at each step. Iterate downward through a hierarchy calling a method at each step.      boundMethod -- This is the bound method of the object you're interested in.     args, kargs -- The arguments and keyword arguments to pass to the         top-level method.      You can call this method via something like this:          inverseExtend(object.method, myArg, myOtherArg)      When calling the method at each step, I'll call it like this:           Class.method(object, *args, **kargs)      Each parent class method *must* be a generator with exactly one yield     statement (even if the yield statement never actually gets called), but the     lowest level class method must *not* be a generator.  In the parent class:              yield args, kargs      should be called when it is time to transfer control to the subclass.  This     may be in the middle of the method or not at all if the parent class does     not wish for the child class's method to get a chance to run. # Build all the necessary data structures. # Figure out the classes in the class hierarchy.  "classes" will # contain the most senior classes first. # Skip classes that don't define the method.  Be careful with getattr # since it automatically looks in parent classes. # Traverse down the class hierarchy.  Watch out for StopIteration's which # signify that the parent does not wish to call the child class's method. # generatorMethods maps generators to methods which we'll need for nice # error messages. # If we didn't have to break, then the lowest level class's method gets to # run. # Traverse back up the class hierarchy.  We should get StopIteration's at # every step. # Test out the code. # I don't have an f method, so you can skip me. \ <A count="0">   <C count="1">     <D count="2" />   </C> </A>