Here is my directory structure:
/home/dmugtasimov/tmp/name-res
root
tests
__init__.py
test_1.py
__init__.py
I simplified the example from the question to demostrate that only four solutions are possible:
from . import some_module
or with more commas from ..
from __future__ import absolute_import
(or use Python 3)What solution is the best? It depends on personal preference of Python 2 or 3. Only the last one is nice and universal for all Pythons. It was really a useful question.
xyz/tests/__init__.py: import xyz.tests.t
xyz/tests/t.py:
import sys
print('sys.path = %s' % sys.path) # see that the parent of "xyz" is on sys.path
print("importing xyz.tests")
import xyz.a
xyz/a.py:
# solution A: absolute_import by __future__ (or use Python 3)
#from __future__ import absolute_import
print("importing xyz.a")
# solution B: explicit relative import
#from . import b # and remove "import xyz.b"
# solution C: relative import (not recommended)
#import b # and remove "import xyz.b"
import xyz.b
xyz/b.py: print("imported xyz.b")
xyz/xyz.py: print("Imported xyz.xyz !!!")
xyz/__init__.py: empty file
Everything possible fails, e.g.
parent_of_xyz=... # The parent directory of "xyz" - absolute path
cd $parent_of_xyz
python -m xyz.tests.t
PYTHONPATH=$parent_of_xyz/xyz python -m unittest tests
PYTHONPATH=$parent_of_xyz python xyz/tests/t.py
with messages like
Imported xyz.xyz !!!
...
ImportError...
If any solution is applied (uncommented), all three examples work.
It can be more simplified without using any subdirectory.
EDIT: I tried yesterday many tests but I wrote it inconsitently from different versions. Excuse me that it was unreproducible from the answer. Now it is fixed.
Do not modify sys.path
it leads to the issues when the same module is available under different names. See Traps for the Unwary.
Use absolute or explicit relative imports instead in the code and run your scripts from the project directory. Run the tests using the full name:
$ python -munittest root.tests.test_1
Some packages do modify sys.path
internally e.g., see how twisted
uses _preamble.py or pypy's autopath.py. You could decide whether their downsides (introduction of obscure import problems) are worth the convience (more ways to run your scripts are allowed). Avoid modifying sys.path
in a code that is used as a library i.e., limit it to test modules and your command-line scripts.