[PATCH 1 of 1] demandimport: allow extensions to import own modules by absolute name
FUJIWARA Katsunori
foozy at lares.dti.ne.jp
Tue Aug 27 02:26:54 CDT 2013
At Mon, 26 Aug 2013 09:53:23 -0400,
Augie Fackler wrote:
>
> On Mon, Aug 26, 2013 at 05:23:58PM +0900, FUJIWARA Katsunori wrote:
> > # HG changeset patch
> > # User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
> > # Date 1377503444 -32400
> > # Mon Aug 26 16:50:44 2013 +0900
> > # Node ID 8901b0024b1713147552e1c7e23113cb901dd705
> > # Parent d4a0055af149cdea20b3136b66cae8a24b2e2a98
> > demandimport: allow extensions to import own modules by absolute name
>
> Sounds pretty useful. Can you rework the test so the module isn't in
> $PWD when hg runs and it tries to import itself? Right now the test
> doesn't actually prove this works, as far as I recall.
OK, I'll resend rewritten one, soon.
> >
> > Before this patch, python modules of each extensions can't import
> > another one in own extension by absolute name, because root modules of
> > each extensions are loaded with "hgext_" prefix.
> >
> > For example, "import extroot.bar" in "extroot/foo.py" of "extroot"
> > extension fails, even though "import bar" in it succeeds.
> >
> > Installing extensions into site-packages of python library path can
> > avoid this problem, but this solution is not reasonable in some cases:
> > using binary package of Mercurial on Windows, for example.
> >
> > This patch retries to import with "hgext_" prefix after ImportError,
> > if the module in the extension may try to import another one in own
> > extension.
> >
> > This patch doesn't change some "_import()"/"_origimport()" invocations
> > below, because ordinary extensions shouldn't cause such invocations.
> >
> > - invocation of "_import()" when root module imports sub-module by
> > absolute path without "fromlist"
> >
> > for example, "import a.b" in "a.__init__.py".
> >
> > extensions can't import in this way without explicit "hgext_"
> > prefix, because they are loaded with such prefix.
> >
> > - invocation of "_origimport()" when "level != -1" with "fromlist"
> >
> > for example, importing after "from __future__ import
> > absolute_import" (level == 0), or "from . import b" or "from .a
> > import b" (0 < level),
> >
> > for portability between python versions and environments,
> > extensions shouldn't cause "level != -1".
> >
> > diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py
> > --- a/mercurial/demandimport.py
> > +++ b/mercurial/demandimport.py
> > @@ -38,6 +38,21 @@
> > else:
> > _import = _origimport
> >
> > +def _hgextimport(importfunc, name, globals, *args):
> > + try:
> > + return importfunc(name, globals, *args)
> > + except ImportError:
> > + if not globals:
> > + raise
> > + # extensions are loaded with "hgext_" prefix
> > + hgextname = 'hgext_%s' % name
> > + nameroot = hgextname.split('.', 1)[0]
> > + contextroot = globals.get('__name__', '').split('.', 1)[0]
> > + if nameroot != contextroot:
> > + raise
> > + # retry to import with "hgext_" prefix
> > + return importfunc(hgextname, globals, *args)
> > +
> > class _demandmod(object):
> > """module demand-loader and proxy"""
> > def __init__(self, name, globals, locals):
> > @@ -55,7 +70,7 @@
> > def _load(self):
> > if not self._module:
> > head, globals, locals, after = self._data
> > - mod = _origimport(head, globals, locals)
> > + mod = _hgextimport(_origimport, head, globals, locals)
> > # load submodules
> > def subload(mod, p):
> > h, t = p, None
> > @@ -92,7 +107,7 @@
> > def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
> > if not locals or name in ignore or fromlist == ('*',):
> > # these cases we can't really delay
> > - return _import(name, globals, locals, fromlist, level)
> > + return _hgextimport(_import, name, globals, locals, fromlist, level)
> > elif not fromlist:
> > # import a [as b]
> > if '.' in name: # a.b
> > @@ -111,7 +126,7 @@
> > # from . import b,c,d or from .a import b,c,d
> > return _origimport(name, globals, locals, fromlist, level)
> > # from a import b,c,d
> > - mod = _origimport(name, globals, locals)
> > + mod = _hgextimport(_origimport, name, globals, locals)
> > # recurse down the module chain
> > for comp in name.split('.')[1:]:
> > if getattr(mod, comp, nothing) is nothing:
> > diff --git a/tests/test-extension.t b/tests/test-extension.t
> > --- a/tests/test-extension.t
> > +++ b/tests/test-extension.t
> > @@ -129,6 +129,73 @@
> > $ echo 'foo = !' >> $HGRCPATH
> > $ echo 'bar = !' >> $HGRCPATH
> >
> > +Check absolute/relative import of extension specific modules
> > +
> > + $ mkdir extroot
> > + $ cat > extroot/__init__.py <<EOF
> > + > extrootstr = 'this is extroot.extrootstr'
> > + > import foo
> > + > def extsetup(ui):
> > + > ui.write('(extroot) ', foo.func(), '\n')
> > + > EOF
> > + $ cat > extroot/foo.py <<EOF
> > + > buf = []
> > + > def func():
> > + > # "not locals" case
> > + > import bar # relative
> > + > buf.append('import bar in func(): %s' % bar.barstr)
> > + > import extroot.bar # absolute
> > + > buf.append('import extroot.bar in func(): %s' % extroot.bar.barstr)
> > + >
> > + > return '\n(extroot) '.join(buf)
> > + >
> > + > # "fromlist == ('*',)" case
> > + > from bar import * # relative
> > + > buf.append('from bar import *: %s' % barstr)
> > + > from extroot.bar import * # absolute
> > + > buf.append('from extroot.bar import *: %s' % barstr)
> > + >
> > + > # "not fromlist" and "if '.' in name" case
> > + > import sub1.baz # relative
> > + > buf.append('import sub1.baz: %s' % sub1.baz.bazstr)
> > + > import extroot.sub1.baz # absolute
> > + > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.bazstr)
> > + >
> > + > # "not fromlist" and NOT "if '.' in name" case
> > + > import sub1 # relative
> > + > buf.append('import sub1: %s' % sub1.sub1str)
> > + > import extroot # absolute
> > + > buf.append('import extroot: %s' % extroot.extrootstr)
> > + >
> > + > # NOT "not fromlist" and NOT "level != -1" case
> > + > from bar import barstr # relative
> > + > buf.append('from bar import barstr: %s' % barstr)
> > + > from extroot.bar import barstr # absolute
> > + > buf.append('from extroot.bar import barstr: %s' % barstr)
> > + > EOF
> > + $ cat > extroot/bar.py <<EOF
> > + > barstr = 'this is extroot.bar.barstr'
> > + > EOF
> > + $ mkdir extroot/sub1
> > + $ cat > extroot/sub1/__init__.py <<EOF
> > + > sub1str = 'this is extroot.sub1.sub1str'
> > + > EOF
> > + $ cat > extroot/sub1/baz.py <<EOF
> > + > bazstr = 'this is extroot.sub1.baz.bazstr'
> > + > EOF
> > + $ hg --config extensions.extroot=extroot tip --template '{rev}\n'
> > + (extroot) from bar import *: this is extroot.bar.barstr
> > + (extroot) from extroot.bar import *: this is extroot.bar.barstr
> > + (extroot) import sub1.baz: this is extroot.sub1.baz.bazstr
> > + (extroot) import extroot.sub1.baz: this is extroot.sub1.baz.bazstr
> > + (extroot) import sub1: this is extroot.sub1.sub1str
> > + (extroot) import extroot: this is extroot.extrootstr
> > + (extroot) from bar import barstr: this is extroot.bar.barstr
> > + (extroot) from extroot.bar import barstr: this is extroot.bar.barstr
> > + (extroot) import bar in func(): this is extroot.bar.barstr
> > + (extroot) import extroot.bar in func(): this is extroot.bar.barstr
> > + 0
> > +
> > $ cd ..
> >
> > hide outer repo
> > _______________________________________________
> > Mercurial-devel mailing list
> > Mercurial-devel at selenic.com
> > http://selenic.com/mailman/listinfo/mercurial-devel
>
----------------------------------------------------------------------
[FUJIWARA Katsunori] foozy at lares.dti.ne.jp
More information about the Mercurial-devel
mailing list