Automates initialization so things are more composable.

    * All specified kwargs in the class and all autoinit classes in
    inheritance hierarchy will be setattr'd at the end of initialization,
    with defaults derived from the inheritance hierarchy as well.
    * If **kwargs is explicitly specified, the __init__ method will be called.
    * If a default is specified as a new[class name] then a default instance
    of that class will be initialized as the value.

        class Base(object):
            @autoinit
            def __init__(self, a="A", **kwargs):
                print "In Base."

        class Base2(object):
            @autoinit
            def __init__(self, a="A2"):
                print "In Base2."

        class T(Base, Base2):
            @autoinit
            def __init__(self, b=new[list], **kwargs):
                print "In T."
        t = T()
        print t.a, t.b
        t = T(['x'])
        print t.a, t.b

def autoinit(fn):
    """Automates initialization so things are more composable.

    * All specified kwargs in the class and all autoinit classes in
    inheritance hierarchy will be setattr'd at the end of initialization,
    with defaults derived from the inheritance hierarchy as well.
    * If **kwargs is explicitly specified, the __init__ method will be called.
    * If a default is specified as a new[class name] then a default instance
    of that class will be initialized as the value.

        class Base(object):
            @autoinit
            def __init__(self, a="A", **kwargs):
                print "In Base."

        class Base2(object):
            @autoinit
            def __init__(self, a="A2"):
                print "In Base2."

        class T(Base, Base2):
            @autoinit
            def __init__(self, b=new[list], **kwargs):
                print "In T."
        t = T()
        print t.a, t.b
        t = T(['x'])
        print t.a, t.b

    """
    if fn is None:
        fn = _empty_init

    if fn_has_args(fn):
        raise Error("*args support is not available in autoinit.")
        # its pretty hard to support this, though doable if really needed...

    __defaults = fn_kwargs(fn)

    avail_ac = fn_available_argcount(fn)
    avail_args = list(fn.__code__.co_varnames[1:avail_ac])

    signature = fn_signature(fn,
        argument_transform=(lambda name: name),
        default_transform=(lambda name, _: "%s=__defaults['%s']" % (name,name)),
        vararg_transform=None,
        kwargs_transform=(lambda _: "**__kwargs"))
    signature[0] = "self"
    
    call_signature = fn_signature(fn,
        argument_transform=(lambda name: "%s=%s" % (name, name)),
        default_transform=(lambda name, _: "%s=%s" % (name,name)),
        vararg_transform=None,
        kwargs_transform=(lambda _: "**__kwargs"))
    call_signature[0] = "self"

    if not fn_has_kwargs(fn):
        signature.append("**__kwargs")
        call_signature.append("**__kwargs")

    signature = ", ".join(signature)
    call_signature = ", ".join(call_signature)
    avail_args = repr(tuple(avail_args))
    
    code = '''def __init__(%(signature)s):
    __cls = self.__class__
    __mro = tuple(__cls.mro())

    # call up the mro
    for __base in __mro:
        if __base is object: continue
        try:
            __wrapped_init = __base.__init__.__wrapped_init
        except AttributeError:
            # not an autoinit class
            pass
        else:
            # **kwargs signals that the initializer wants to be called
            if __wrapped_init and fn_has_kwargs(__wrapped_init):
                __wrapped_init(%(call_signature)s)

    # get defaults from hierarchy
    __update_kwargs = { }
    for __base in reversed(__mro):
        if __base is __cls or __base is object: continue
        try:
            __defaults = __base.__init__.__defaults
        except AttributeError:
            # not an autoinit class
            pass
        else:
            for __name, __val in __defaults.iteritems():
                if __val is not Default:
                    __update_kwargs[__name] = __val

    # get locally passed arguments into __update_kwargs
    __locals = locals()
    for __name in %(avail_args)s:
        __val = __locals[__name]
        if __val is Default:
            if __name not in __update_kwargs:
                raise Error("Must specify argument " + __name)
        else:
            __update_kwargs[__name] = __val
            
    for __name, __val in __kwargs.iteritems():
        if __val is Default:
            if __name not in __update_kwargs:
                raise Error("Must specify argument " + __name)
        else:
            __update_kwargs[__name] = __val

    # set attributes according to kwargs
    for __name, __val in __update_kwargs.iteritems():
        if isinstance(__val, _new_initializer):
            setattr(self, __name, __val())
        else:
            setattr(self, __name, __val)
''' % locals()
    exec(code, globals(), locals())
    #
    # i know, exec -- no other way to get the signature to match it seems
    # unless i build it out of an abstract syntax tree or something, which 
    # seems excessive. or i could inspect the signature and do stuff dynamically
    # but that is troublesome and the documentation generators won't like it
    #
    # if you want to try to fix it to not use exec but retain the semantics
    # please do.
    #
    # -cyrus
    
    init = eval('__init__')
    init.__wrapped_init = fn #@UndefinedVariable
    init.__defaults = __defaults #@UndefinedVariable
    _functools.update_wrapper(init, fn) #@UndefinedVariable
    return init #@UndefinedVariable