"""
apipkg: control the exported namespace of a python package.

see http://pypi.python.org/pypi/apipkg

(c) holger krekel, 2009 - MIT license
"""
import os
import sys
from types import ModuleType

__version__ = "1.1"

def initpkg(pkgname, exportdefs, attr=dict()):
    """ initialize given package from the export definitions. """
    oldmod = sys.modules[pkgname]
    d = {}
    f = getattr(oldmod, '__file__', None)
    if f:
        f = os.path.abspath(f)
    d['__file__'] = f
    if hasattr(oldmod, '__version__'):
        d['__version__'] = oldmod.__version__
    if hasattr(oldmod, '__loader__'):
        d['__loader__'] = oldmod.__loader__
    if hasattr(oldmod, '__path__'):
        d['__path__'] = [os.path.abspath(p) for p in oldmod.__path__]
    if hasattr(oldmod, '__doc__'):
        d['__doc__'] = oldmod.__doc__
    d.update(attr)
    oldmod.__dict__.update(d)
    mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d)
    sys.modules[pkgname]  = mod

def importobj(modpath, attrname):
    module = __import__(modpath, None, None, ['__doc__'])
    if not attrname:
        return module

    retval = module
    names = attrname.split(".")
    for x in names:
        retval = getattr(retval, x)
    return retval

class ApiModule(ModuleType):
    def __init__(self, name, importspec, implprefix=None, attr=None):
        self.__name__ = name
        self.__all__ = [x for x in importspec if x != '__onfirstaccess__']
        self.__map__ = {}
        self.__implprefix__ = implprefix or name
        if attr:
            for name, val in attr.items():
                #print "setting", self.__name__, name, val
                setattr(self, name, val)
        for name, importspec in importspec.items():
            if isinstance(importspec, dict):
                subname = '%s.%s'%(self.__name__, name)
                apimod = ApiModule(subname, importspec, implprefix)
                sys.modules[subname] = apimod
                setattr(self, name, apimod)
            else:
                parts = importspec.split(':')
                modpath = parts.pop(0)
                attrname = parts and parts[0] or ""
                if modpath[0] == '.':
                    modpath = implprefix + modpath
                if name == '__doc__':
                    self.__doc__ = importobj(modpath, attrname)
                else:
                    self.__map__[name] = (modpath, attrname)

    def __repr__(self):
        l = []
        if hasattr(self, '__version__'):
            l.append("version=" + repr(self.__version__))
        if hasattr(self, '__file__'):
            l.append('from ' + repr(self.__file__))
        if l:
            return '<ApiModule %r %s>' % (self.__name__, " ".join(l))
        return '<ApiModule %r>' % (self.__name__,)

    def __makeattr(self, name):
        """lazily compute value for name or raise AttributeError if unknown."""
        #print "makeattr", self.__name__, name
        target = None
        if '__onfirstaccess__' in self.__map__:
            target = self.__map__.pop('__onfirstaccess__')
            importobj(*target)()
        try:
            modpath, attrname = self.__map__[name]
        except KeyError:
            if target is not None and name != '__onfirstaccess__':
                # retry, onfirstaccess might have set attrs
                return getattr(self, name)
            raise AttributeError(name)
        else:
            result = importobj(modpath, attrname)
            setattr(self, name, result)
            try:
                del self.__map__[name]
            except KeyError:
                pass # in a recursive-import situation a double-del can happen
            return result

    __getattr__ = __makeattr

    def __dict__(self):
        # force all the content of the module to be loaded when __dict__ is read
        dictdescr = ModuleType.__dict__['__dict__']
        dict = dictdescr.__get__(self)
        if dict is not None:
            hasattr(self, 'some')
            for name in self.__all__:
                try:
                    self.__makeattr(name)
                except AttributeError:
                    pass
        return dict
    __dict__ = property(__dict__)

@@ -2228,16 +2228,293 @@
 modpath%0A
+%0A                if not attrname:%0A                    subname = '%25s.%25s'%25(self.__name__, name)%0A                    apimod = AliasModule(subname, modpath)%0A                    sys.modules%5Bsubname%5D = apimod%0A                    setattr(self, name, apimod)%0A                else:%0A    
         
@@ -2539,24 +2539,28 @@
  '__doc__':%0A
+    
             
@@ -2619,38 +2619,46 @@
                 
+    
 else:%0A
+    
                 
@@ -4489,8 +4489,1071 @@
 dict__)%0A
+%0Aclass AliasModule(ModuleType):%0A    def __init__(self, name, modpath):%0A        self.__name__ = name%0A        self.__modpath = modpath%0A%0A    def __repr__(self):%0A        l = %5B%5D%0A        if hasattr(self, '__version__'):%0A            l.append(%22version=%22 + repr(self.__version__))%0A        if hasattr(self, '__file__'):%0A            l.append('from ' + repr(self.__file__))%0A        if l:%0A            return '%3CAliasModule %25r %25s%3E' %25 (self.__name__, %22 %22.join(l))%0A        return '%3CAliasModule %25r%3E' %25 (self.__name__,)%0A%0A    def __getattr__(self, name):%0A        mod = importobj(self.__modpath, None)%0A        result = getattr(mod, name)%0A        setattr(self, name, result)%0A        for k, v in mod.__dict__.items():%0A            setattr(self, k, v)%0A        return result%0A%0A    def __dict__(self):%0A        # force all the content of the module to be loaded when __dict__ is read%0A        dictdescr = ModuleType.__dict__%5B'__dict__'%5D%0A        dict = dictdescr.__get__(self)%0A        if dict is not None:%0A            hasattr(self, 'some')%0A        return dict%0A    __dict__ = property(__dict__)%0A

make importing from an alias module work.