# HG changeset patch -- Bitbucket.org # Project pylib # URL http://bitbucket.org/hpk42/pylib/overview # User holger krekel <hol...@merlinux.eu> # Date 1289635306 -3600 # Node ID 1371dfb06957338aef2066da0af04888f7e2af55 # Parent 4ce4019bb0a929796cacb251eb1505001cdaacee use new apipkg with aliasmodule support
--- a/py/_apipkg.py +++ b/py/_apipkg.py @@ -9,11 +9,11 @@ import os import sys from types import ModuleType -__version__ = "1.1" +__version__ = '1.2.dev5' 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: @@ -25,10 +25,11 @@ def initpkg(pkgname, exportdefs, attr=di 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 @@ -44,6 +45,16 @@ def importobj(modpath, attrname): 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__'] @@ -65,8 +76,13 @@ class ApiModule(ModuleType): 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) @@ -118,3 +134,28 @@ class ApiModule(ModuleType): pass return dict __dict__ = property(__dict__) + + +def AliasModule(modname, modpath): + mod = [] + + def getmod(): + if not mod: + mod.append(importobj(modpath, None)) + return mod[0] + + class AliasModule(ModuleType): + + def __repr__(self): + return '<AliasModule %r for %r>' % (modname, modpath) + + 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) --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ def main(): long_description = long_description, install_requires=['py>=1.3.9', ], # force newer py version which removes 'py' namespace # # so we can occupy it - version='2.0.0.dev6', + version='2.0.0.dev7', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], --- a/testing/test_apipkg.py +++ b/testing/test_apipkg.py @@ -1,7 +1,7 @@ import types import sys import py -from py import apipkg +import py._apipkg as apipkg import subprocess # # test support for importing modules @@ -16,7 +16,7 @@ class TestRealModule: tfile = pkgdir.join('__init__.py') tfile.write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, { 'x': { 'module': { @@ -89,7 +89,7 @@ class TestScenarios: def test_relative_import(self, monkeypatch, tmpdir): pkgdir = tmpdir.mkdir("mymodule") pkgdir.join('__init__.py').write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, exportdefs={ '__doc__': '.submod:maindoc', 'x': '.submod:x', @@ -109,32 +109,45 @@ class TestScenarios: def test_recursive_import(self, monkeypatch, tmpdir): pkgdir = tmpdir.mkdir("recmodule") pkgdir.join('__init__.py').write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, exportdefs={ 'some': '.submod:someclass', }) """)) 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" def test_module_alias_import(self, monkeypatch, tmpdir): pkgdir = tmpdir.mkdir("aliasimport") pkgdir.join('__init__.py').write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, exportdefs={ 'some': 'os.path', }) """)) 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 py._apipkg as 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 @@ -211,14 +224,22 @@ def test_initpkg_transfers_attrs(monkeyp 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') @@ -249,7 +270,7 @@ def test_name_attribute(): def test_error_loading_one_element(monkeypatch, tmpdir): pkgdir = tmpdir.mkdir("errorloading1") pkgdir.join('__init__.py').write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, exportdefs={ 'x': '.notexists:x', 'y': '.submod:y' @@ -267,7 +288,7 @@ def test_error_loading_one_element(monke def test_onfirstaccess(tmpdir, monkeypatch): pkgdir = tmpdir.mkdir("firstaccess") pkgdir.join('__init__.py').write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, exportdefs={ '__onfirstaccess__': '.submod:init', 'l': '.submod:l', @@ -276,7 +297,7 @@ def test_onfirstaccess(tmpdir, monkeypat """)) pkgdir.join('submod.py').write(py.code.Source(""" l = [] - def init(): + def init(): l.append(1) """)) monkeypatch.syspath_prepend(tmpdir) @@ -288,19 +309,19 @@ def test_onfirstaccess(tmpdir, monkeypat @py.test.mark.multi(mode=['attr', 'dict', 'onfirst']) def test_onfirstaccess_setsnewattr(tmpdir, monkeypatch, mode): - pkgname = 'mode_' + mode + pkgname = tmpdir.basename.replace("-", "") pkgdir = tmpdir.mkdir(pkgname) pkgdir.join('__init__.py').write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, exportdefs={ '__onfirstaccess__': '.submod:init', }, ) """)) 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) @@ -329,12 +350,11 @@ def test_bpython_getattr_override(tmpdir def test_chdir_with_relative_imports_shouldnt_break_lazy_loading(tmpdir): - from py import _apipkg # cause py.apipkg is a apimodule - tmpdir.join('apipkg.py').write(py.code.Source(_apipkg)) + tmpdir.join('apipkg.py').write(py.code.Source(apipkg)) pkg = tmpdir.mkdir('pkg') messy = tmpdir.mkdir('messy') pkg.join('__init__.py').write(py.code.Source(""" - import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, { 'test': '.sub:test', }) @@ -365,7 +385,7 @@ def test_chdir_with_relative_imports_sho def test_dotted_name_lookup(tmpdir, monkeypatch): pkgdir = tmpdir.mkdir("dotted_name_lookup") pkgdir.join('__init__.py').write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, dict(abs='os:path.abspath')) """)) monkeypatch.syspath_prepend(tmpdir) @@ -375,9 +395,76 @@ def test_dotted_name_lookup(tmpdir, monk def test_extra_attributes(tmpdir, monkeypatch): pkgdir = tmpdir.mkdir("extra_attributes") pkgdir.join('__init__.py').write(py.code.Source(""" - from py import apipkg + import py._apipkg as apipkg apipkg.initpkg(__name__, dict(abs='os:path.abspath'), dict(foo='bar')) """)) monkeypatch.syspath_prepend(tmpdir) import extra_attributes assert extra_attributes.foo == 'bar' + +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 py._apipkg as 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 py._apipkg as 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 --- a/py/__init__.py +++ b/py/__init__.py @@ -8,40 +8,44 @@ dictionary or an import path. (c) Holger Krekel and others, 2004-2010 """ -__version__ = '2.0.0.dev6' +__version__ = '2.0.0.dev7' from py import _apipkg -_apipkg.initpkg(__name__, attr={'_apipkg': _apipkg}, exportdefs=dict( +_apipkg.initpkg(__name__, attr={'_apipkg': _apipkg}, exportdefs={ # access to all standard lib modules - std = '._std:std', + 'std': '._std:std', # access to all posix errno's as classes - error = '._error:error', + 'error': '._error:error', - _pydir = '.__metainfo:pydir', - version = 'py:__version__', # backward compatibility + '_pydir' : '.__metainfo:pydir', + 'version': 'py:__version__', # backward compatibility - test = 'pytest', # defer to pytest package + # pytest-2.0 has a flat namespace, we use alias modules + # to keep old references compatible + 'test' : 'pytest', + 'test.collect' : 'pytest', + 'test.cmdline' : 'pytest', # hook into the top-level standard library - process = { + 'process' : { '__doc__' : '._process:__doc__', 'cmdexec' : '._process.cmdexec:cmdexec', 'kill' : '._process.killproc:kill', 'ForkedFunc' : '._process.forkedfunc:ForkedFunc', }, - apipkg = { + 'apipkg' : { 'initpkg' : '._apipkg:initpkg', 'ApiModule' : '._apipkg:ApiModule', }, - iniconfig = { + 'iniconfig' : { 'IniConfig' : '._iniconfig:IniConfig', 'ParseError' : '._iniconfig:ParseError', }, - path = { + 'path' : { '__doc__' : '._path:__doc__', 'svnwc' : '._path.svnwc:SvnWCCommandPath', 'svnurl' : '._path.svnurl:SvnCommandPath', @@ -50,7 +54,7 @@ _apipkg.initpkg(__name__, attr={'_apipkg }, # python inspection/code-generation API - code = { + 'code' : { '__doc__' : '._code:__doc__', 'compile' : '._code.source:compile_', 'Source' : '._code.source:Source', @@ -69,7 +73,7 @@ _apipkg.initpkg(__name__, attr={'_apipkg }, # backports and additions of builtins - builtin = { + 'builtin' : { '__doc__' : '._builtin:__doc__', 'enumerate' : '._builtin:enumerate', 'reversed' : '._builtin:reversed', @@ -98,7 +102,7 @@ _apipkg.initpkg(__name__, attr={'_apipkg }, # input-output helping - io = { + 'io' : { '__doc__' : '._io:__doc__', 'dupfile' : '._io.capture:dupfile', 'TextIO' : '._io.capture:TextIO', @@ -113,7 +117,7 @@ _apipkg.initpkg(__name__, attr={'_apipkg }, # small and mean xml/html generation - xml = { + 'xml' : { '__doc__' : '._xmlgen:__doc__', 'html' : '._xmlgen:html', 'Tag' : '._xmlgen:Tag', @@ -122,7 +126,7 @@ _apipkg.initpkg(__name__, attr={'_apipkg 'escape' : '._xmlgen:escape', }, - log = { + 'log' : { # logging API ('producers' and 'consumers' connected via keywords) '__doc__' : '._log:__doc__', '_apiwarn' : '._log.warning:_apiwarn', @@ -137,11 +141,11 @@ _apipkg.initpkg(__name__, attr={'_apipkg }, # compatibility modules (deprecated) - compat = { + 'compat' : { '__doc__' : '._compat:__doc__', 'doctest' : '._compat.dep_doctest:doctest', 'optparse' : '._compat.dep_optparse:optparse', 'textwrap' : '._compat.dep_textwrap:textwrap', 'subprocess' : '._compat.dep_subprocess:subprocess', }, -)) +}) _______________________________________________ py-svn mailing list py-svn@codespeak.net http://codespeak.net/mailman/listinfo/py-svn