[PATCH] demandimport: store level argument on _demandmod instances

Dan Villiom Podlaski Christiansen danchr at gmail.com
Tue Aug 17 10:48:48 CDT 2010


# HG changeset patch
# User Dan Villiom Podlaski Christiansen <danchr at gmail.com>
# Date 1282059970 -7200
# Node ID 837e75faec6467cd2d6c51354d9ee02855af784f
# Parent  98149ae642259e899e4440738bedac51f723f5ae
demandimport: store level argument on _demandmod instances

The 'level' argument to __import__ was added in Python 2.6, and is
specified for either relative or absolute imports. The fix introduced
in e160f2312815 allowed such imports to proceed without failure, but
effectively disabled demandimport for them. This is particularly
unfortunate in Python 3.x, where *all* imports are either relative or
absolute.

The solution introduced here is to store the level argument on the
demandimport instance, and propagate it to _origimport() when its
value isn't None.

Please note that this patch hasn't been tested in Python 3.x, and thus
may not be complete. I'm worried about how sub-imports are handled; I
don't know what they are, or whether the level argument should be
modified for them. I've added 'TODO' notes to these cases; hopefully,
someone more knowledgable of these issues will deal with them.

diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py
--- a/mercurial/demandimport.py
+++ b/mercurial/demandimport.py
@@ -29,29 +29,35 @@ _origimport = __import__
 
 class _demandmod(object):
     """module demand-loader and proxy"""
-    def __init__(self, name, globals, locals):
+    def __init__(self, name, globals, locals, level):
         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
+            if level is not None:
+                mod = _origimport(head, globals, locals, level)
+            else:
+                mod = _origimport(head, globals, locals)
             # load submodules
             def subload(mod, p):
                 h, t = p, None
                 if '.' in p:
                     h, t = p.split('.', 1)
                 if not hasattr(mod, h):
-                    setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
+                    # TODO: should we adjust the level here?
+                    submod = _demandmod(p, mod.__dict__, mod.__dict__,
+                                        level=level)
+                    setattr(mod, h, submod)
                 elif t:
                     subload(getattr(mod, h), t)
 
@@ -91,28 +97,36 @@ def _demandimport(name, globals=None, lo
             base, rest = name.split('.', 1)
             # email.__init__ loading email.mime
             if globals and globals.get('__name__', None) == base:
-                return _origimport(name, globals, locals, fromlist)
+                if level is not None:
+                    return _origimport(name, globals, locals, fromlist, level)
+                else:
+                    return _origimport(name, globals, locals, fromlist)
             # if a is already demand-loaded, add b to its submodule list
             if base in locals:
                 if isinstance(locals[base], _demandmod):
                     locals[base]._extend(rest)
                 return locals[base]
-        return _demandmod(name, globals, locals)
+        return _demandmod(name, globals, locals, level=level)
     else:
+        # from a import b,c,d
         if level is not None:
-            # 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 = _origimport(name, globals, locals, level=level)
+        else:
+            mod = _origimport(name, globals, locals)
         # recurse down the module chain
         for comp in name.split('.')[1:]:
             if not hasattr(mod, comp):
-                setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
+                # TODO: should we adjust the level here?
+                submod = _demandmod(comp, mod.__dict__, mod.__dict__,
+                                    level=level)
+                setattr(mod, comp, submod)
             mod = getattr(mod, comp)
         for x in fromlist:
             # set requested submodules for demand load
             if not(hasattr(mod, x)):
-                setattr(mod, x, _demandmod(x, mod.__dict__, locals))
+                # TODO: should we adjust the level here?
+                submod = _demandmod(x, mod.__dict__, locals, level=level)
+                setattr(mod, x, submod)
         return mod
 
 ignore = [


More information about the Mercurial-devel mailing list