Commits

Ronny Pfannschmidt committed cc4e57f Merge

merge mainline

  • Participants
  • Parent commits 94d9234, 7ecd687

Comments (0)

Files changed (5)

+1.2.dev
+----------------------------------------
+
+- Allow to import from Aliasmodules  (thanks Ralf Schmitt)
+- avoid loading __doc__ early, so ApiModule is now fully lazy
+
 1.1
 ----------------------------------------
 
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+     
+  The above copyright notice and this permission notice shall be included in all
+  copies or substantial portions of the Software.
+ 
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
 import sys
 from types import ModuleType
 
-__version__ = "1.1"
+__version__ = '1.2.dev7'
 
 def initpkg(pkgname, exportdefs, attr=dict()):
     """ initialize given package from the export definitions. """
-    oldmod = sys.modules[pkgname]
+    oldmod = sys.modules.get(pkgname)
     d = {}
     f = getattr(oldmod, '__file__', None)
     if f:
         d['__loader__'] = oldmod.__loader__
     if hasattr(oldmod, '__path__'):
         d['__path__'] = [os.path.abspath(p) for p in oldmod.__path__]
-    if hasattr(oldmod, '__doc__'):
+    if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None):
         d['__doc__'] = oldmod.__doc__
     d.update(attr)
-    oldmod.__dict__.update(d)
+    if hasattr(oldmod, "__dict__"):
+        oldmod.__dict__.update(d)
     mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d)
     sys.modules[pkgname]  = mod
 
     return retval
 
 class ApiModule(ModuleType):
+    def __docget(self):
+        try:
+            return self.__doc
+        except AttributeError:
+            if '__doc__' in self.__map__:
+                return self.__makeattr('__doc__')
+    def __docset(self, value):
+        self.__doc = value
+    __doc__ = property(__docget, __docset)
+
     def __init__(self, name, importspec, implprefix=None, attr=None):
         self.__name__ = name
         self.__all__ = [x for x in importspec if x != '__onfirstaccess__']
                 attrname = parts and parts[0] or ""
                 if modpath[0] == '.':
                     modpath = implprefix + modpath
-                if name == '__doc__':
-                    self.__doc__ = importobj(modpath, attrname)
+
+                if not attrname:
+                    subname = '%s.%s'%(self.__name__, name)
+                    apimod = AliasModule(subname, modpath)
+                    sys.modules[subname] = apimod
+                    if '.' not in name:
+                        setattr(self, name, apimod)
                 else:
                     self.__map__[name] = (modpath, attrname)
 
                     pass
         return dict
     __dict__ = property(__dict__)
+
+
+def AliasModule(modname, modpath, attrname=None):
+    mod = []
+
+    def getmod():
+        if not mod:
+            x = importobj(modpath, None)
+            if attrname is not None:
+                x = getattr(x, attrname)
+            mod.append(x)
+        return mod[0]
+
+    class AliasModule(ModuleType):
+
+        def __repr__(self):
+            x = modpath
+            if attrname:
+                x += "." + attrname
+            return '<AliasModule %r for %r>' % (modname, x)
+
+        def __getattribute__(self, name):
+            return getattr(getmod(), name)
+
+        def __setattr__(self, name, value):
+            setattr(getmod(), name, value)
+
+        def __delattr__(self, name):
+            delattr(getmod(), name)
+
+    return AliasModule(modname)
 except ImportError:
     from distutils.core import setup
 
-from apipkg import __version__
-
 def main():
     setup(
         name='apipkg',
         description=
         'apipkg: namespace control and lazy-import mechanism',
         long_description = open('README.txt').read(),
-        version= __version__,
+        version='1.2.dev7',
         url='http://bitbucket.org/hpk42/apipkg',
         license='MIT License',
         platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

File test_apipkg.py

             })
         """))
         pkgdir.join('submod.py').write(py.code.Source("""
-            import recmodule 
+            import recmodule
             class someclass: pass
             print (recmodule.__dict__)
         """))
         monkeypatch.syspath_prepend(tmpdir)
-        import recmodule 
+        import recmodule
         assert isinstance(recmodule, apipkg.ApiModule)
         assert recmodule.some.__name__ == "someclass"
 
         """))
         monkeypatch.syspath_prepend(tmpdir)
         import aliasimport
-        assert aliasimport.some is py.std.os.path
+        for k, v in py.std.os.path.__dict__.items():
+            assert getattr(aliasimport.some, k) == v
+
+    def test_from_module_alias_import(self, monkeypatch, tmpdir):
+        pkgdir = tmpdir.mkdir("fromaliasimport")
+        pkgdir.join('__init__.py').write(py.code.Source("""
+            import apipkg
+            apipkg.initpkg(__name__, exportdefs={
+                'some': 'os.path',
+            })
+        """))
+        monkeypatch.syspath_prepend(tmpdir)
+        from fromaliasimport.some import join
+        assert join is py.std.os.path.join
 
 def xtest_nested_absolute_imports():
     import email
     assert newmod.__loader__ == mod.__loader__
     assert newmod.__doc__ == mod.__doc__
 
-def test_initpkg_not_overwrite_exportdefs(monkeypatch):
+def test_initpkg_nodoc(monkeypatch):
     mod = type(sys)('hello')
-    mod.__doc__ = "this is the documentation"
+    mod.__file__ = "hello.py"
     monkeypatch.setitem(sys.modules, 'hello', mod)
+    apipkg.initpkg('hello', {})
+    newmod = sys.modules['hello']
+    assert not newmod.__doc__
+
+def test_initpkg_overwrite_doc(monkeypatch):
+    hello = type(sys)('hello')
+    hello.__doc__ = "this is the documentation"
+    monkeypatch.setitem(sys.modules, 'hello', hello)
     apipkg.initpkg('hello', {"__doc__": "sys:__doc__"})
-    newmod = sys.modules['hello']
-    assert newmod != mod
-    assert newmod.__doc__ == sys.__doc__
+    newhello = sys.modules['hello']
+    assert newhello != hello
+    assert newhello.__doc__ == sys.__doc__
 
 def test_initpkg_not_transfers_not_existing_attrs(monkeypatch):
     mod = type(sys)('hello')
     """))
     pkgdir.join('submod.py').write(py.code.Source("""
         l = []
-        def init(): 
+        def init():
             l.append(1)
     """))
     monkeypatch.syspath_prepend(tmpdir)
         )
     """))
     pkgdir.join('submod.py').write(py.code.Source("""
-        def init(): 
+        def init():
             import %s as pkg
-            pkg.newattr = 42 
+            pkg.newattr = 42
     """ % pkgname))
     monkeypatch.syspath_prepend(tmpdir)
     mod = __import__(pkgname)
     monkeypatch.syspath_prepend(tmpdir)
     import extra_attributes
     assert extra_attributes.foo == 'bar'
+
+def test_aliasmodule_aliases_an_attribute():
+    am = apipkg.AliasModule("mymod", "pprint", 'PrettyPrinter')
+    r = repr(am)
+    assert "<AliasModule 'mymod' for 'pprint.PrettyPrinter'>" == r
+    assert am.format
+
+def test_aliasmodule_repr():
+    am = apipkg.AliasModule("mymod", "sys")
+    r = repr(am)
+    assert "<AliasModule 'mymod' for 'sys'>" == r
+    am.version
+    assert repr(am) == r
+
+def test_aliasmodule_proxy_methods(tmpdir, monkeypatch):
+    pkgdir = tmpdir
+    pkgdir.join('aliasmodule_proxy.py').write(py.code.Source("""
+        def doit():
+            return 42
+    """))
+
+    pkgdir.join('my_aliasmodule_proxy.py').write(py.code.Source("""
+        import apipkg
+        apipkg.initpkg(__name__, dict(proxy='aliasmodule_proxy'))
+
+        def doit():
+            return 42
+    """))
+
+    monkeypatch.syspath_prepend(tmpdir)
+    import aliasmodule_proxy as orig
+    from my_aliasmodule_proxy import proxy
+
+    doit = proxy.doit
+    assert doit is orig.doit
+
+    del proxy.doit
+    py.test.raises(AttributeError, "orig.doit")
+
+    proxy.doit = doit
+    assert orig.doit is doit
+
+def test_aliasmodule_nested_import_with_from(tmpdir, monkeypatch):
+    import os
+    pkgdir = tmpdir.mkdir("api1")
+    pkgdir.ensure("__init__.py").write(py.std.textwrap.dedent("""
+        import apipkg
+        apipkg.initpkg(__name__, {
+            'os2': 'api2',
+            'os2.path': 'api2.path2',
+            })
+    """))
+    tmpdir.join("api2.py").write(py.std.textwrap.dedent("""
+        import os, sys
+        from os import path
+        sys.modules['api2.path2'] = path
+        x = 3
+    """))
+    monkeypatch.syspath_prepend(tmpdir)
+    from api1 import os2
+    from api1.os2.path import abspath
+    assert abspath == os.path.abspath
+    # check that api1.os2 mirrors os.*
+    assert os2.x == 3
+    import api1
+    assert 'os2.path' not in api1.__dict__
+
+
+def test_initpkg_without_old_module():
+    apipkg.initpkg("initpkg_without_old_module",
+                   dict(modules="sys:modules"))
+    from initpkg_without_old_module import modules
+    assert modules is sys.modules