Reputation: 21
I am trying to dynamically import functions from a file in a subdirectory.
For example:
workspace
--|main.py
----|subfolder1
------|subfolder2
--------|file1.py
--------|file2.py
The file main.py
would dynamically run a function in file1.py
or file2.py
. The function in the 2 files are different contents but the same name, I'll call it run
for this example.
I am trying to do this so I can have the user input a file they want to run and it will run. The main reason why I chose dynamic imports is because I will be adding files to the list of available files to run every day and I don't want to have or add to a large if statement.
I'm trying to use the importlib
module to dynamically import this.
# main.py
file = input("Either file1 or file2")
try:
mod = importlib.import_module(f"subfolder1.subfolder2.{file}")
except Exception as error:
print(f"[ERROR] {error}!")
else:
mod.run()
The error I get is
[ERROR] No module named 'subfolder1.subfolder2.file1.py'; 'subfolder1.subfolder2.file1' is not a package!
Upvotes: 0
Views: 52
Reputation: 9997
Read your error message carefully-
[ERROR] No module named 'subfolder1.subfolder2.file1.py'; 'subfolder1.subfolder2.file1' is not a package!
This happened because you called importlib like this-
importlib.import_module("subfolder1.subfolder2.file1.py")
What is confusing you- import_module
is NOT looking for a file. It is looking for a module
- using a dotted path expression. This is saying "look for a package called subfolder1, with a subpackage called subfolder2, with a subpackage called file1, with a module called py"
When you issue that command, python will search every directory in your sys.path
for a package named subfolder1
, and then try to import subfolder2.file1.py
from it.
the question with "init.py" is that python treats those a bit differently. Imagine your PYTHONPATH included two folders- first
and second
(in that order). you have a file called second/package/module.py
, and an empty folder called first/package
. If you do NOT have init.py file in first/package
python will NOT consider first/package
as a package, and will continue searching and allow you to import package.module
from second
. If you DO have the __init__.py
file in first/package
, import package.module
will fail.
When you try to import something, your search path is important- you have to make sure that for any dotted name "a.b", that the directory that "a" lives in is on your PYTHONPATH (sys.path). You can modify sys.path at runtime to make that happen.
Ultimately though, the reason your code didn't work is that you included .py
in the import name. If your user wants you to import somepackage/somefile.py
your import command is somepacakge.somefile
(with no .py
)
For your specific case (wanting to import random files) you are probably better off NOT including __init__.py
files. In fact, your best bet is likely something like this-
import sys
from importlib import Path
target_file = Path("somepath/somefile.py")
target_path = str(target_file.parent)
# ensure the directory your file lives in is first
# on the search path
sys.path.insert(0, target_path)
# import your target file
importlib.import_module(target.stem)
# restore the search path to its previous state
sys.path.pop(0)
(worth mentioning- its rather undefined what happens stuff your target files are trying to import)
Upvotes: 0
Reputation: 21
I have fixed my issue, it turns out it was what I was inputting into the importlib.import_module
.
The answer to the question tho, should be adding __init__.py
files into the directories that will be packages. The __init__.py
files are fine being empty but can have contents within.
Helpful links
Thank you @joanis for the solution to my problem and thank you to everyone else who helped for the solution to the original question.
Upvotes: 0