demandimport: import sub-module relatively as expected (issue5208)
authorFUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Sat, 06 Aug 2016 22:24:33 +0900
changeset 29748 14f077f7519a
parent 29747 919a4b7f531d
child 29749 ae9a4d6a8d51
demandimport: import sub-module relatively as expected (issue5208) Before this patch, importing sub-module might (1) fail or (2) success but import incorrect module, because demandimport tries to import sub-module with level=-1 (on Python 2.x) or level=0 (on Python 3.x), which is default value of "level" argument at construction of "_demandmod" proxy object. (1) on Python 3.x, importing sub-module always fails to import existing sub-module (2) both on Python 2.x and 3.x, importing sub-module might import same name module on root level unintentionally On Python 2.x, existing sub-module is prior to this unexpected module. Therefore, this problem hasn't appeared. To import sub-module relatively as expected, this patch specifies "1" as import level explicitly at construction of "_demandmod" proxy object for sub-module.
mercurial/demandimport.py
tests/test-extension.t
--- a/mercurial/demandimport.py	Sat Aug 06 15:00:34 2016 -0700
+++ b/mercurial/demandimport.py	Sat Aug 06 22:24:33 2016 +0900
@@ -117,7 +117,8 @@
                 if '.' in p:
                     h, t = p.split('.', 1)
                 if getattr(mod, h, nothing) is nothing:
-                    setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
+                    setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__,
+                                               level=1))
                 elif t:
                     subload(getattr(mod, h), t)
 
@@ -210,8 +211,8 @@
             mod = rootmod
             for comp in modname.split('.')[1:]:
                 if getattr(mod, comp, nothing) is nothing:
-                    setattr(mod, comp,
-                            _demandmod(comp, mod.__dict__, mod.__dict__))
+                    setattr(mod, comp, _demandmod(comp, mod.__dict__,
+                                                  mod.__dict__, level=1))
                 mod = getattr(mod, comp)
             return mod
 
--- a/tests/test-extension.t	Sat Aug 06 15:00:34 2016 -0700
+++ b/tests/test-extension.t	Sat Aug 06 22:24:33 2016 +0900
@@ -432,6 +432,36 @@
   REL: this is absextroot.xsub1.xsub2.called.func()
   REL: this relimporter imports 'this is absextroot.relimportee'
 
+Examine whether sub-module is imported relatively as expected.
+
+See also issue5208 for detail about example case on Python 3.x.
+
+  $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py
+  $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found
+
+  $ cat > $TESTTMP/notexist.py <<EOF
+  > text = 'notexist.py at root is loaded unintentionally\n'
+  > EOF
+
+  $ cat > $TESTTMP/checkrelativity.py <<EOF
+  > from mercurial import cmdutil
+  > cmdtable = {}
+  > command = cmdutil.command(cmdtable)
+  > 
+  > # demand import avoids failure of importing notexist here
+  > import extlibroot.lsub1.lsub2.notexist
+  > 
+  > @command('checkrelativity', [], norepo=True)
+  > def checkrelativity(ui, *args, **opts):
+  >     try:
+  >         ui.write(extlibroot.lsub1.lsub2.notexist.text)
+  >         return 1 # unintentional success
+  >     except ImportError:
+  >         pass # intentional failure
+  > EOF
+
+  $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity)
+
 #endif
 
   $ cd ..