Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I run a Python 3 script with imports from a subfolder?

Whatever I try, since I switched to Python 3 I can run scripts with imports only from the root folder of the project, but not from subfolders. I am aware that there are lots of questions here about the error messages I get, but the proposed solutions don't work for me. Could someone please provide a sample solution for this small sample project? I am sure it would be appreciated by many.

proj
├── foofolder
│   ├── __init__.py
│   └── foofile.py
├── subfolder
│   ├── __init__.py
│   └── run.py
└── __init__.py

I define the function foofun() in foofile.py, and want to call it in run.py.

If run.py is directly in proj it works. But (just to keep things organized) I want to have it in a subfolder - which surprisingly seems impossible.

The annoying thing is that autocomplete in my IDE (PyCharm) suggests that from foofolder.foofile import foofun should work. But it does not. Nor does any other import I could imagine:

from foofolder.foofile import foofun --> ImportError: No module named 'foofolder'

from .foofolder.foofile import foofun --> SystemError: Parent module '' not loaded, cannot perform relative import (Same with two dots at the beginning.)

from proj.foofolder.foofile import foofun --> ImportError: No module named 'proj'

Not even the absolute import works. Why does it not find proj?

sys.path is: ['/parent/proj/subfolder', '/parent/env/lib/python35.zip', '/parent/env/lib/python3.5', '/parent/env/lib/python3.5/plat-x86_64-linux-gnu', '/parent/env/lib/python3.5/lib-dynload', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/parent/env/lib/python3.5/site-packages']

I use Python 3.5.2 in a virtualenv.


Edit: I was wrong when I suggested that this problem is specific to Python 3. Problem and solution were the same when I checked in Python 2.7.12.

The solutions given by Pevogam work. The first one literally adds just '..' to 'sys.path', which describes the parent folder of wherever run.py is executed. The second explicitly adds '/parent/proj'.

like image 513
Watchduck Avatar asked Oct 29 '22 17:10

Watchduck


2 Answers

The quickest way I can think about doing this from the top of my head would be:

# subfolder/run.py
import sys
sys.path.append("..")

from foofolder.foofile import foofun

foofun()

Notice however that this will only work if you run run.py from its folder. A more elaborate way that does not depend on this would be

# subfolder/run.py
import sys
import os.path as o
sys.path.append(o.abspath(o.join(o.dirname(sys.modules[__name__].__file__), "..")))

from foofolder.foofile import foofun

foofun()

where you could reach the function from any location. The easiest thing to do for all such modules is to use the same pointer to the root package which in your cases happens by adding "..". You can then perform any imports from the perspective of this root package.

I tend to avoid using relative imports since they are confusing and reduce readability but hopefully some of this helps.

[*] Some IDEs may perform their own project scanning which is the reason they might still find the import as long as you run the python code within the IDE.

like image 65
pevogam Avatar answered Nov 14 '22 02:11

pevogam


Q: How run python script in subfolder?

A: Call using PYTHONPATH=. python3 subfolder/run.py

Add to bashrc: alias pys = PYTHONPATH=. python3. Then pys subfolder/run.py works!

Comment: For me a script is something that just executes tasks. It is never called from something else or imported. I have many of them. Some of them are similar (e.g. taskA_reset.py, taskB_reset.py). I prefer to organize this as taskA/reset.py et cetera. But, then it is no longer possible to run them! But, prepending the path is a simple solution and it does not require me to change any of my python-code.

like image 33
Hunaphu Avatar answered Nov 14 '22 01:11

Hunaphu