[PATCH RFC] demandimport: store level argument on _demandmod instances

Dan Villiom Podlaski Christiansen danchr at gmail.com
Mon Aug 16 17:09:06 CDT 2010


# HG changeset patch
# User Dan Villiom Podlaski Christiansen <danchr at gmail.com>
# Date 1281996413 -7200
# Node ID aa7ec092847b5fd281d219b4de797c6476159e9c
# Parent  f8ec22c7dedaf3fae1b767d6681381a391760e5f
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 such imports. This is
particularly unfortunate in Python 3.x, where *all* imports are either
relative or absolute.

The solution introduce 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.

Note that this patch also contains test code: a relative import in
commands & and a print in help.

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -10,7 +10,7 @@ from lock import release
 from i18n import _, gettext
 import os, re, sys, difflib, time, tempfile
 import hg, util, revlog, bundlerepo, extensions, copies, error
-import patch, help, mdiff, url, encoding, templatekw, discovery
+from . import patch, help, mdiff, url, encoding, templatekw, discovery
 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
 import merge as mergemod
 import minirst, revset
diff --git a/mercurial/demandimport.py b/mercurial/demandimport.py
--- a/mercurial/demandimport.py
+++ b/mercurial/demandimport.py
@@ -29,29 +29,34 @@ _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__))
+                    submod = _demandmod(p, mod.__dict__, mod.__dict__,
+                                        level=level)
+                    setattr(mod, h, submod)
                 elif t:
                     subload(getattr(mod, h), t)
 
@@ -91,28 +96,34 @@ 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__))
+                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))
+                submod = _demandmod(x, mod.__dict__, locals, level=level)
+                setattr(mod, x, submod)
         return mod
 
 ignore = [
diff --git a/mercurial/help.py b/mercurial/help.py
--- a/mercurial/help.py
+++ b/mercurial/help.py
@@ -9,6 +9,7 @@ from i18n import gettext, _
 import sys, os
 import extensions
 
+sys.stderr.write('42\n')
 
 def moduledoc(file):
     '''return the top-level python documentation for the given file


More information about the Mercurial-devel mailing list