def pipeline_property(name, **kwargs):
    """
    Create a property accessor for the given name.  The property will
    dig through the bound instance on which it was accessed for an
    attribute "app" and check that object for an attribute of the given
    name.  If the "app" object does not have such an attribute, it will
    look for an attribute "app" on THAT object and continue it's search
    from there.  If the named attribute cannot be found accessing the
    property will raise AttributeError.

    If a default kwarg is provided you get that instead of the
    AttributeError.  When found the attribute will be cached on instance
    with the property accessor using the same name as the attribute
    prefixed with a leading underscore.
    """

    cache_attr_name = '_%s' % name

    def getter(self):
        cached_value = getattr(self, cache_attr_name, None)
        if cached_value:
            return cached_value
        app = self  # first app is on self
        while True:
            app = getattr(app, 'app', None)
            if not app:
                break
            try:
                value = getattr(app, name)
            except AttributeError:
                continue
            setattr(self, cache_attr_name, value)
            return value
        if 'default' in kwargs:
            return kwargs['default']
        raise AttributeError('No apps in pipeline have a '
                             '%s attribute' % name)

    return property(getter)