[PATCH] demandimport: honor absolute_import (issue4029)

Jason R. Coombs jaraco at jaraco.com
Fri Sep 6 20:48:02 CDT 2013


# HG changeset patch
# User Jason R. Coombs <jaraco at jaraco.com>
# Date 1378518380 14400
#      Fri Sep 06 21:46:20 2013 -0400
# Node ID 6b00771ea6ce22dc3dda8b6da063d3112bfcbce8
# Parent  1d07bf106c2ad1c7ef5e257e754ca8d858bd04b0
demandimport: honor absolute_import (issue4029)

diff -r 1d07bf106c2a -r 6b00771ea6ce mercurial/demandimport.py
--- a/mercurial/demandimport.py Wed Sep 04 18:42:55 2013 -0700
+++ b/mercurial/demandimport.py Fri Sep 06 21:46:20 2013 -0400
@@ -25,6 +25,7 @@
 '''

 import __builtin__
+import __future__
 _origimport = __import__

 nothing = object()
@@ -55,7 +56,7 @@
     def _load(self):
         if not self._module:
             head, globals, locals, after = self._data
-            mod = _origimport(head, globals, locals)
+            mod = _origimport(head, globals, locals, [], self._level())
             # load submodules
             def subload(mod, p):
                 h, t = p, None
@@ -74,6 +75,18 @@
                 locals[head] = mod
             object.__setattr__(self, "_module", mod)

+    def _level(self):
+        """
+        Determine the 'level' parameter to the __import__ function, based on
+        self._data.
+        """
+        head, globals, locals, after = self._data
+        abs_import = globals.get('absolute_import', None)
+        if isinstance(abs_import, __future__._Feature):
+            # absolute_import was indicated, so disallow relative imports
+            return 0
+        return -1
+
     def __repr__(self):
         if self._module:
             return "<proxied module '%s'>" % self._data[0]
@@ -81,7 +94,7 @@
     def __call__(self, *args, **kwargs):
         raise TypeError("%s object is not callable" % repr(self))
     def __getattribute__(self, attr):
-        if attr in ('_data', '_extend', '_load', '_module'):
+        if attr in ('_data', '_extend', '_load', '_module', '_level'):
             return object.__getattribute__(self, attr)
         self._load()
         return getattr(self._module, attr)
diff -r 1d07bf106c2a -r 6b00771ea6ce tests/test-demandimport-absolute.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-demandimport-absolute.py       Fri Sep 06 21:46:20 2013 -0400
@@ -0,0 +1,46 @@
+"test demand import when absolute_import is indicated"
+
+import os
+import sys
+import shutil
+import textwrap
+import unittest
+
+import silenttestrunner
+
+from mercurial import demandimport
+
+class TestAbsoluteImport(unittest.TestCase):
+
+    def setUp(self):
+        os.mkdir('pkg')
+        f = open('pkg/mod.py', 'w')
+        f.write(textwrap.dedent("""
+            from __future__ import absolute_import
+            import os
+            """).lstrip())
+        f.close()
+        f = open('pkg/__init__.py', 'w')
+        f.close()
+        f = open('pkg/os.py', 'w')
+        f.write('''val = "this is not the module you're looking for"\n''')
+        f.close()
+        demandimport.enable()
+
+    def tearDown(self):
+        shutil.rmtree('pkg')
+        demandimport.disable()
+
+    def test_absolute_import(self):
+        if sys.version_info < (2,5):
+            print("Test only viable on Python 2.5 or later")
+            return
+        import pkg.mod
+        # trigger the loading of the module
+        pkg.mod.__name__
+        assert pkg.mod.os.__name__ == 'os'
+        assert 'devnull' in dir(pkg.mod.os)
+        assert 'val' not in dir(pkg.mod.os)
+
+if __name__ == '__main__':
+    silenttestrunner.main(__name__)


More information about the Mercurial-devel mailing list