Python unittest failing to resolve import statements - python

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

Related

Import a submodule in a Python module

I have already read Python: importing a sub‑package or sub‑module but this:
file content
============================================================================
main.py from mymodule.a import A; A()
mymodule/submodule/__init__.py (empty)
mymodule/submodule/b.py class B: pass
mymodule/__init__.py (empty)
mymodule/a.py (see below)
mymodule/a.py:
import submodule
class A:
def __init__(self):
self.b = submodule.B()
fails with:
File "a.py", line 1, in
import submodule
ModuleNotFoundError: No module named 'submodule'
when lauching the script main.py.
Question: how should I import submodule in mymodule/a.py?
TLDR:
mymodule/a.py:
from . import submodule
# or
import mymodule.submodule as submodule
Once you run your main.py, python adds to your sys.path the path to main.py folder. Python can now search subfolders to find modules trying to be imported. Once you import a.py, python DOES NOT add anything else to sys.path, therefore, to be able import subfolders, you need to do either relative importing (from . import submodule), which you can do because you aren't running a.py as your main file, OR do a full import, doing import mymodule.submodule, since python can search starting on main.py folder.

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

Import python files from git submodule without sys.path.append

I have a git project ('submodule_folder') cloned as a submodule inside my git project ('my_project') with the following file structure:
my_project
submodule_folder
submodule_folder2
__init__.py
submodule_file.py
file_to_import.py
my_file1.py
my_file2.py
I want to import 'file_to_import.py' from 'my_file1.py' but 'file_to_import.py' imports 'submodule_file.py'.
When doing the following import from 'my_file1.py' I get a ModuleNotFoundError: No module named 'submodule_folder2'.
from submodule_folder import file_to_import
Using Python3, is there any way to do this import without writing sys.path.append('submodule_folder/') in each of my files? I'd prefer not to modify the content of 'submodule_folder'.
If file_to_import.py has
from submodule_folder2 import submodule_file
it worked in Python 2 because in Py2 it was implicit relative import. Python 3 disabled this import mode, it only allows absolute import or explicit relative import. Either
from submodule_folder.submodule_folder2 import submodule_file
or
from .submodule_folder2 import submodule_file

Python imports with __init__.py

No matter how I structure the imports in the code files and in the __init__.py files, I can't seem to get it right for executing the program and running the tests using pytest. How do I need to write the imports when my project structure looks like this:
src/
__init__.py
VocableFileWriter.py
WordListHelper.py
WordListReader.py
XLDAttributeValueAdder.py
exceptions/
__init__.py
XMLInvalidException.py
XMLParseException.py
gui/
__init__.py
GTKSignal.py
XLDAttributeValueAdderWindow.py
test/
__init__.py
test_XLDAttributeValueAdder.py
xmlparser/
__init__.py
XMLParser.py
Currently I have them like this:
In the __init__.py files I have the imports like this (src/__init__.py):
from src import *
from src.exceptions import *
from src.xmlparser import *
and in a subpackage (src/xmlparser/__init__.py):
from src.xmlparser import *
So I guess those are project-absolute paths to the modules.
In the code files themselves I import like this:
import os
import sys
from VocableFileWriter import VocableFileWriter
from XLDAttributeValueAdder import XLDAttributeValueAdder
However, when I execute the code from the directory above src using:
./src/main.py
It tells me that:
Traceback (most recent call last):
File "./src/main.py", line 6, in <module>
from VocableFileWriter import VocableFileWriter
File "/home/xiaolong/Development/PycharmProjects/xld-attribute-value-adder/src/VocableFileWriter.py", line 2, in <module>
from XMLInvalidException import XMLInvalidException
ImportError: No module named 'XMLInvalidException'
It used to be a PyCharm project, but I couldn't get it to run with the imports structure PyCharm used when not using PyCharm but running it from the terminal, so I decided I wanted to take the whole import stuff into my own hands. So don't get confused by it being in a PyCharm directory.
I also want to be able to execute the tests using for example:
py.test src/test/test_XLDAttributeValueAdder.py
How do I solve this mess?
Edit#1:
I had the program running once, but then the test complained about missing modules, probably because it's in another subdirectory and I tried so many configurations, that I don't know how I had the program running anymore. If possible please add some explanation why a certain structure is correct and works for both, tests and the program itself.
EDIT#2:
I've managed to get the tests running now, but now the program doesn't run anymore.
I emptied all the __init__.py files and used only project-absolute paths in the code files like this (src/test/test_XLDAttributeValueAdder.py):
from src.VocableFileWriter import VocableFileWriter
from src.WordListHelper import WordListHelper
from src.WordListReader import WordListReader
from src.XLDAttributeValueAdder import XLDAttributeValueAdder
from src.exceptions.XMLInvalidException import XMLInvalidException
from src.exceptions.XMLParseException import XMLParserException
from src.xmlparser.XMLParser import XMLParser
from src.test.path_helper import go_up
from src.test.path_helper import go_in
and in the main.py:
from src.VocableFileWriter import VocableFileWriter
from src.XLDAttributeValueAdder import XLDAttributeValueAdder
the output of ./src/main.py:
./src/main.py
Traceback (most recent call last):
File "./src/main.py", line 6, in <module>
from src.VocableFileWriter import VocableFileWriter
ImportError: No module named 'src'
EDIT#3:
I've tried the relative import way like this:
main.py moved one directory up
The structure now looks like this:
main.py
src/
__init__.py
VocableFileWriter.py
WordListHelper.py
WordListReader.py
XLDAttributeValueAdder.py
exceptions/
__init__.py
XMLInvalidException.py
XMLParseException.py
gui/
__init__.py
GTKSignal.py
XLDAttributeValueAdderWindow.py
test/
__init__.py
test_XLDAttributeValueAdder.py
xmlparser/
__init__.py
XMLParser.py
__init__.py files empty
main.py file gets relative
Imports look like this:
import os
import sys
from .src.VocableFileWriter import VocableFileWriter
from .src.XLDAttributeValueAdder import XLDAttributeValueAdder
When I try to run the main.py file:
Traceback (most recent call last):
File "main.py", line 6, in <module>
from .src.VocableFileWriter import VocableFileWriter
SystemError: Parent module '' not loaded, cannot perform relative import
However, there is a __init__.py file in the same directory as the main.py file, also empty.
You'd better use relative imports.
When doing from src import * in src/__init__.py you'll only import anything you've defined before calling the import. Most likely that's just nothing. If you've defined the __all__ variable, you'll get the submodule from it, but maybe also an AttributeError if a variable hasn't been defined yet.
So instead import the modules you need explicitly where you need them like
from .VocableFileWriter import VocableFileWriter
from .exceptions.XMLInvalidException import XMLInvalidException
or lets say within src/gui/GTKSignal.py
from ..exceptions.XMLInvalidException import XMLInvalidException
Also you could use project-absolute paths like mentioned in your Edit#2.
Furthermore you've a problem with your path when calling ./src/main.py. In this case the directory containing src is not in the path. Instead ./src/ is in the path. Therefore python doesn't consider it a package anymore and since there's no other package with that name available, the import fails.
Instead put your main.py module in the same directory like the src package or call it as a module with python -m src.main. Doing so you could also rename it to src/main.py and call the package instad with python -m src.
When using the first approach, main should use an absolute import structure. Just think of it as a module you put anywhere on your computer, while your src package is somewhere else, where it can be found by Python, like any other module or package installed by the system. It's not different, when it's located besides the package itself, since the current directory is in sys.path.
main.py
import os
import sys
from src.VocableFileWriter import VocableFileWriter
from src.XLDAttributeValueAdder import XLDAttributeValueAdder

ImportError: cannot import module - Only one module can not be imported from package

I created a package, for the ease of use I call it packageA.
In this package I have 4 submodules in (separate files) and an init file, so the package contains the following files:
__init__.py
moduleA.py
moduleB.py
moduleC.py
moduleD.py
Module B-D stand alone, they don't import any other local module. Module A imports module B-D.
Now I have a script that wants to import packageA, the init.py is empty so far.
import packageA
works without problems. But moduleA is not available from this import. If I want to use it via packageA.moduleA it raises this error:
AttributeError: 'module' object has no attribute
The following also works without problems:
from packageA import moduleB
from packageA import moduleC
from packageA import moduleD
And the next import causes the ImportError:
from packageA import moduleA
raises this Error:
Traceback (most recent call last):
File "run.py", line 19, in <module>
from packageA import moduleA
ImportError: cannot import name moduleA
I thought that maybe I'm doing sth wrong in moduleA, but even if moduleA is empty the import raises the same error. It seems like that this module can't be imported somehow. How is this possible?
I have separete unit test files for all python modules, and yes, moduleA is working correctly without any errors.
Perhaps you are importing a different version of the package from a different location. Try this in your script to make sure it is the right one -- it should print the full path of the __init__.py file.
import packageA
print packageA.__file__
Also, check if the package's module search path has been altered. Normally this is a list that contains one element, the directory path of the package:
print packageA.__path__

Categories