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.
--- 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 ..