Import a package beyond top-level package using importlib - python

I am looking to import a package (plugin) outside my current directory. Say I have a directory structure:
/plugins
/test2
__init__.py
test_file.py
/src
main.py
The main.py has the following:
import importlib
import pkgutil
import os
import sys
module_dir = os.path.join(os.path.dirname(__file__) + "/../plugins")
for finder, name, ispkg in pkgutil.iter_modules([module_dir]):
print(finder, name, ispkg)
test = importlib.import_module(name, package='..plugins')
print(test.test_file.test_variable)
The test_file.py has the following:
test_variable = "testing"
With this current code, I would get an error of:
ModuleNotFoundError: No module named 'test2'
When I try to add sys.path.append(module_dir) so the path is included, I would get an error:
ValueError: attempted relative import beyond top-level package
What would be the best way to implement this and import the package/plugin? Assuming I could have multiple package under the plugin folder

Related

ModuleNotFoundError: Even with __init__.py file

I have 3 python file :
test.py
modules(folder) and in modules there 3 to files : module1.py module2.py init.py
test.py
./test.py
./modules
./modules/module1.py
./modules/module2.py
./modules/__init__.py
module1.py:
from module2 import temp
def print_temp():
print(temp)
if __name__=='__main__':
print_temp()
module2.py
temp =1
test.py
from modules.module1 import print_temp
print_temp()
When I run python test.py I got ModuleNotFoundError: No module named 'module2'
How can I fix that please?
When you import a module in Python, it searches the PYTHONPATH for a module / package with that name. But it doesn't search inside directories so you have to specify.
If you want to be able to run modules.module1 directly (as well as import it) then you must use the full path:
from modules.module2 import temp
But if you just want to be able to import the module and not run it directly it is better to use a relative import as it will still work even if the name of the package is changed:
from .module2 import temp

relative import with no known parent package

I'm trying to import an class into my test (test_account.py), but I get the following error:
from ..src.account_status import TestClass
ImportError: attempted relative import with no known parent package
This is my directory I have scr and test in the same App directory.
./App/src/account_status.py
./App/test/test_account.py
My import in test_account.py:
from ..src.account_status import TestClass
I have tried adding __init__.py to both folders and the App directory, but I get the same message, why?
Try this
import sys
sys.path.append("../src")
from account_status import TestClass
sys.path.remove("../src")

How to import python module from a different directory into jupyter notebook

I have a project which is structured as below:
/project
/src
__init__.py
my_module.py
/notebooks
my_notebook.ipynb
I wanted to import my_module.py in my_notebook.ipynb, so I first tried import src.my_module getting ModuleNotFoundError: No module named 'src' and from ..src import my_module getting ImportError: attempted relative import with no known parent package. From several similar topics I found that adding /project location to SYSPATH would be a quick hack and it works indeed:
import sys
sys.path.insert(1, '/project')
from src import my_module
However, interfering with SYSPATH seems to be a nasty way and I wonder - does a safer/cleaner solution exists. Also, I don't quite get why from ..src import my_module doesn't work in the first place?

Python unittest failing to resolve import statements

I have a file structure that looks like the following
project
src
__init__.py
main.py
module.py
secondary.py
test
test_module.py
module.py
import secondary
x = False
secondary.py
pass
test_module.py
from unittest import TestCase
from src import module
class ModuleTest(TestCase):
def test_module(self):
self.assertTrue(module.x)
Invoking python3 -m unittest discover in /project/ gives an error:
File "/Users/Me/Code/project/test/test_module.py", line 6, in <module>
from src import module
File "/Users/Me/Code/project/src/module.py", line 1, in <module>
import secondary
ImportError: No module named 'secondary'
What can I do so that secondary.py is imported without error?
In Python 3 (and Python 2 with from __future__ import absolute_import), you must be explicit about what module you want when importing another module from the same package. The syntax you're using in module.py (import secondary) only works if secondary is a top-level module in a folder in the Python module search path.
To explicitly request a relative import from your own package, use from . import secondary instead. Or, make an absolute import, using the name of the package as well as the module (from src import secondary, or import src.secondary and use src.secondary elsewhere the module instead of just secondary).
Try adding this to the top of the test file
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
Note: Depending on the project structure we have to specify the root folder

ImportError when importing from another module

My app structure is the following:
./mod1/__init__.py
./mod1/utils.py
./mod2/__init__.py
./mod2/test.py
Now in ./mod2/test.py I do:
from mod1 import utils
But I get an ImportError that no module is named utils. What's wrong?!
In order for this to work, the parent directory of mod1 and mod2 must be in sys.path, which probably means that it needs to be in the environment variable PYTHONPATH. Please see the module search path documentation.
One solution that does not require modification of PYTHONPATH is to place your executable script in the parent directory of mod1 and mod2.
Add a top-level folder in the sys.path:
import sys
sys.path.append('path_to_app_folder')
You should write this line before from mod1 import utils. Also add __init__.py:
./mod1/__init__.py
./mod1/utils.py
./mod2/__init__.py
./mod2/test.py
__init__.py
You can dynamically get the path to the app folder from ./mod2/test.py, using os.path.abspath. So, you code for ./mod2/test.py will look like this:
import os
import sys
top_level_folder = os.path.abspath('../')
sys.path.append(top_level_folder)
from mod1 import utils

Categories