2

This question is related to Can't import module with importlib.import_module, which I don't think got to the root of the issue at all.

I'm trying to understand the reason for differences with importlib used in the interactive interpreter vs. a non-interactive script, because I'm getting different results with each.

I have a folder containing myscript.py, and I want it to import a module called module from a subfolder, my_subdir of the initial working directory. I want to do this by moving into that directory and working there. I know there are other solutions to accessing a module in a subdirectory, but this is the use case that I'd like to talk about.

The following is the contents of myscript.py, which simply moves into the subdirectory and tries to import the module from there, and then call some hello-world function from the module:

#!/home/chris/anaconda2/bin/python
import sys
import platform
import os
import importlib

print("Python version "+ platform.python_version())
print(sys.path)

os.chdir('my_subdir')
my_module = importlib.import_module('module')
my_module.my_function()

When I run this from the command line, Python can't find the module. It seems that the import system doesn't know about the change of directory from os.chdir():

(base) chris@linux-om3m:~/workspace> python myscript.py 
Python version 2.7.15
['/home/chris/workspace', '/home/chris/anaconda2/lib/python27.zip', '/home/chris/anaconda2/lib/python2.7', '/home/chris/anaconda2/lib/python2.7/plat-linux2', '/home/chris/anaconda2/lib/python2.7/lib-tk', '/home/chris/anaconda2/lib/python2.7/lib-old', '/home/chris/anaconda2/lib/python2.7/lib-dynload', '/home/chris/anaconda2/lib/python2.7/site-packages', '/home/chris/anaconda2/lib/python2.7/site-packages']
Traceback (most recent call last):
  File "myscript.py", line 11, in <module>
    my_module = importlib.import_module('module')
  File "/home/chris/anaconda2/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
ImportError: No module named module

However, when I type the same script into the interactive interpreter, it works fine:

(base) chris@linux-om3m:~/workspace> python
Python 2.7.15 | packaged by conda-forge | (default, Jul  2 2019, 00:39:44) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> import platform
>>> import os
>>> import importlib
>>> print("Python version "+ platform.python_version())
Python version 2.7.15
>>> print(sys.path)
['', '/home/chris/anaconda2/lib/python27.zip', '/home/chris/anaconda2/lib/python2.7', '/home/chris/anaconda2/lib/python2.7/plat-linux2', '/home/chris/anaconda2/lib/python2.7/lib-tk', '/home/chris/anaconda2/lib/python2.7/lib-old', '/home/chris/anaconda2/lib/python2.7/lib-dynload', '/home/chris/anaconda2/lib/python2.7/site-packages', '/home/chris/anaconda2/lib/python2.7/site-packages']
>>> os.chdir('my_subdir')
>>> my_module = importlib.import_module('module')
>>> my_module.my_function()
Here I am!
>>> 

I can fix this issue by manually updating sys.path with a sys.path.append(os.getcwd()) after I do the os.chdir - but I do not understand why this is only necessary in non-interactive mode.

The only thing I do notice (and is printed here in the pasted output) is that under the interactive interpreter, sys.path has an empty-string entry, whereas in non-interactive mode it contains the working dir where I invoked python.

I know I could fix this problem using relative imports, and for now I'm fixing the problem using a sys.path.append(os.getcwd()), but I'd like to understand why there is a difference between running interactively vs. non-interactively.

I'm getting the same results with Python 2.7 and Python 3.6.

carthurs
  • 553
  • 1
  • 5
  • 18
  • 1
    Actually, I can also fix it by `sys.path.append('')` in non-interactive mode, so I guess `''` has a special meaning. But the question remains as to why the interactive interpreter has the empty string on `sys.path`, but the non-interactive does not (and thus we have quite different behaviour between the two!). – carthurs Oct 28 '19 at 14:52

1 Answers1

2

As you noted in your comment, appending '' to sys.path fixes the issue. This is because '' represents your cwd, so os.chdir() knows where to find your subdirectory my_subdir correctly (see Why is the first element in python's sys.path an empty string?).

Using an absolute path for my_subdir should also fix your issue.

casper.dcl
  • 13,035
  • 4
  • 31
  • 32
  • This makes sense - but why is the behaviour not the same between the two cases? Why does the non-interactive version not use a `''` too? – carthurs Oct 28 '19 at 15:36
  • Ah ok https://docs.python.org/3/library/sys.html#sys.path - `''` is used if the script directory is not available - e.g. in interactive mode. This makes sense, but ultimately this causes unintended (and surprising!) differing behaviour. – carthurs Oct 28 '19 at 15:42