# HG changeset patch # User FUJIWARA Katsunori # Date 2013-10-04 16:02:22 # Node ID e3a5922e18c3bfe914a9a100546cc2c631339409 # Parent 8bbe208c181228b7974b98e62e1732d2263d6b4d 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 diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py --- a/mercurial/demandimport.py +++ b/mercurial/demandimport.py @@ -40,22 +40,23 @@ else: 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 @@ def _demandimport(name, globals=None, lo 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 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,45 @@ Check hgweb's load order: $ 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 < from __future__ import absolute_import + > import ambig # should load "libroot/ambig.py" + > s = ambig.s + > EOF + $ cat > loadabs.py < 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 < import ambig # should load "libroot/mod/ambig.py" + > s = ambig.s + > EOF + $ cat > loadrel.py < 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