Patchwork [2,of,3,V3] demandimport: support "absolute_import" for external libraries (issue4029)

login
register
mail settings
Submitter Katsunori FUJIWARA
Date Sept. 7, 2013, 6:22 p.m.
Message ID <13017746a8ec1cce8826.1378578172@feefifofum>
Download mbox | patch
Permalink /patch/2400/
State Superseded
Commit e3a5922e18c3bfe914a9a100546cc2c631339409
Headers show

Comments

Katsunori FUJIWARA - Sept. 7, 2013, 6:22 p.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1378572147 -32400
#      Sun Sep 08 01:42:27 2013 +0900
# Node ID 13017746a8ec1cce88260292178fe50a77cd0aa0
# Parent  84e640cc281766ec91c9c2a3a264ccff3ba5010c
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

Patch

diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py
--- a/mercurial/demandimport.py
+++ b/mercurial/demandimport.py
@@ -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
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,36 @@ 
   $ echo 'foo = !' >> $HGRCPATH
   $ echo 'bar = !' >> $HGRCPATH
 
+Check "from __future__ import absolute_import" support for external libraries
+
+#if absimport
+  $ 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
+  $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
+  > from __future__ import absolute_import
+  > import ambig # shoud load "libroot/ambig.py"
+  > s = ambig.s
+  > EOF
+  $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
+  > import ambig # should load "libroot/mod/ambig.py"
+  > s = ambig.s
+  > EOF
+  $ cat > absload.py <<EOF
+  > import mod.ambigabs as ambigabs
+  > import mod.ambigrel as ambigrel
+  > def extsetup():
+  >     print 'ambigabs.s=%s' % ambigabs.s
+  >     print 'ambigrel.s=%s' % ambigrel.s
+  > EOF
+  $ (PYTHONPATH=$PYTHONPATH:$TESTTMP/libroot; hg --config extensions.absload=absload.py id --id)
+  ambigabs.s=libroot/ambig.py
+  ambigrel.s=libroot/mod/ambig.py
+  c24b9ac61126
+#endif
+
   $ cd ..
 
 hide outer repo