Mercurial > hg-stable
changeset 19932:e3a5922e18c3
demandimport: support "absolute_import" for external libraries (issue4029)
Before this patch, demandimport of Mercurial may fail to load external
libraries using "from __future__ import absolute_import": for example,
importing "foo" in "bar.baz" module will load "bar.foo" if it exists,
even though "absolute_import" is enabled in "bar.baz" module.
So, extensions for Mercurial can't use such external libraries.
This patch saves "level" of import request for on-demand module
loading in the future: default value of level is -1, and level is 0
when "absolute_import" is enabled.
"level" value is passed to built-in import function in
"_demandmod._load()" and it should load target module correctly.
This patch changes only one "_demandmod" construction case other than
cases below:
- construction in "_demandmod._load()"
this code path should be used only in relative sub-module
loading case
- constructions other than patched one in"_demandimport()"
these code paths shouldn't be used in "level != -1" case
author | FUJIWARA Katsunori <foozy@lares.dti.ne.jp> |
---|---|
date | Sat, 05 Oct 2013 01:02:22 +0900 |
parents | 8bbe208c1812 |
children | 621a26eb3a99 |
files | mercurial/demandimport.py tests/test-extension.t |
diffstat | 2 files changed, 45 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/demandimport.py Sat Oct 05 01:02:22 2013 +0900 +++ b/mercurial/demandimport.py Sat Oct 05 01:02:22 2013 +0900 @@ -40,22 +40,23 @@ class _demandmod(object): """module demand-loader and proxy""" - def __init__(self, name, globals, locals): + def __init__(self, name, globals, locals, level=-1): if '.' in name: head, rest = name.split('.', 1) after = [rest] else: head = name after = [] - object.__setattr__(self, "_data", (head, globals, locals, after)) + object.__setattr__(self, "_data", + (head, globals, locals, after, level)) object.__setattr__(self, "_module", None) def _extend(self, name): """add to the list of submodules to load""" self._data[3].append(name) def _load(self): if not self._module: - head, globals, locals, after = self._data - mod = _origimport(head, globals, locals) + head, globals, locals, after, level = self._data + mod = _import(head, globals, locals, None, level) # load submodules def subload(mod, p): h, t = p, None @@ -105,7 +106,7 @@ if isinstance(locals[base], _demandmod): locals[base]._extend(rest) return locals[base] - return _demandmod(name, globals, locals) + return _demandmod(name, globals, locals, level) else: if level != -1: # from . import b,c,d or from .a import b,c,d
--- a/tests/test-extension.t Sat Oct 05 01:02:22 2013 +0900 +++ b/tests/test-extension.t Sat Oct 05 01:02:22 2013 +0900 @@ -129,6 +129,45 @@ $ echo 'foo = !' >> $HGRCPATH $ echo 'bar = !' >> $HGRCPATH +Check "from __future__ import absolute_import" support for external libraries + + $ mkdir $TESTTMP/libroot + $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py + $ mkdir $TESTTMP/libroot/mod + $ touch $TESTTMP/libroot/mod/__init__.py + $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py + +#if absimport + $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF + > from __future__ import absolute_import + > import ambig # should load "libroot/ambig.py" + > s = ambig.s + > EOF + $ cat > loadabs.py <<EOF + > import mod.ambigabs as ambigabs + > def extsetup(): + > print 'ambigabs.s=%s' % ambigabs.s + > EOF + $ (PYTHONPATH=$PYTHONPATH:$TESTTMP/libroot; hg --config extensions.loadabs=loadabs.py root) + ambigabs.s=libroot/ambig.py + $TESTTMP/a +#endif + +#if no-py3k + $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF + > import ambig # should load "libroot/mod/ambig.py" + > s = ambig.s + > EOF + $ cat > loadrel.py <<EOF + > import mod.ambigrel as ambigrel + > def extsetup(): + > print 'ambigrel.s=%s' % ambigrel.s + > EOF + $ (PYTHONPATH=$PYTHONPATH:$TESTTMP/libroot; hg --config extensions.loadrel=loadrel.py root) + ambigrel.s=libroot/mod/ambig.py + $TESTTMP/a +#endif + $ cd .. hide outer repo